Module smart_config::validation

source ·
Expand description

Parameter and config validation and filtering.

§Overview

The core validation functionality is encapsulated in the Validate trait.

§Examples

§Validation

use secrecy::{ExposeSecret, SecretString};
use smart_config::validation;

#[derive(DescribeConfig, DeserializeConfig)]
#[config(validate(
    Self::validate_secret_key,
    "secret key must have expected length"
))]
struct ValidatedConfig {
    secret_key: SecretString,
    /// Reference key length. If specified, the secret key length
    /// will be checked against it.
    #[config(validate(..=100))]
    // ^ Validates that the value is in the range. Note that validations
    // handle `Option`s intelligently; if the value isn't specified
    // (i.e., is `None`), it will pass validation.
    secret_key_len: Option<usize>,
    #[config(validate(not_empty, "must not be empty"))]
    app_name: String,
}

// We have to use `&String` rather than more idiomatic `&str` in order to
// exactly match the validated type.
fn not_empty(s: &String) -> bool {
    !s.is_empty()
}

impl ValidatedConfig {
    fn validate_secret_key(&self) -> Result<(), ErrorWithOrigin> {
        if let Some(expected_len) = self.secret_key_len {
            let actual_len = self.secret_key.expose_secret().len();
            if expected_len != actual_len {
                return Err(ErrorWithOrigin::custom(format!(
                    "unexpected `secret_key` length ({actual_len}); \
                     expected {expected_len}"
                )));
            }
        }
        Ok(())
    }
}

§Filtering

Filtering reuses the Validate trait, but rather than failing, converts a value to None.

use smart_config::validation;

#[derive(DescribeConfig, DeserializeConfig)]
struct FilteringConfig {
    /// Will convert `url: ''` to `None`.
    #[config(deserialize_if(validation::NotEmpty))]
    url: Option<String>,
    /// Will convert either of `env: ''` or `env: 'unset'` to `None`.
    #[config(deserialize_if(valid_env, "not empty or 'unset'"))]
    env: Option<String>,
}

fn valid_env(s: &String) -> bool {
    !s.is_empty() && s != "unset"
}

// Base case: no filtering.
let env = smart_config::Environment::from_iter("", [
    ("URL", "https://example.com"),
    ("ENV", "prod"),
]);
let config: FilteringConfig = testing::test_complete(env)?;
assert_eq!(config.url.unwrap(), "https://example.com");
assert_eq!(config.env.unwrap(), "prod");

// Filtering applied to both params.
let env = smart_config::Environment::from_iter("", [
    ("URL", ""),
    ("ENV", "unset"),
]);
let config: FilteringConfig = testing::test_complete(env)?;
assert_eq!(config.url, None);
assert_eq!(config.env, None);

Structs§

  • Validates that a string or a data collection (e.g., Vec) is not empty.

Traits§

  • Generic post-validation for a configuration parameter or a config.