tier 0.1.17

Rust configuration library for layered TOML, env, and CLI settings
Documentation
use std::collections::{BTreeMap, BTreeSet};

use crate::loader::SourceKind;

mod config;
mod display;
mod env;
mod field;
mod impls;
mod merge;
mod paths;
mod prefix;
mod validation;

pub use self::env::EnvDecoder;
pub use self::merge::MergeStrategy;
pub use self::prefix::prefixed_metadata;
pub use self::validation::{
    ValidationCheck, ValidationLevel, ValidationNumber, ValidationRule, ValidationRuleConfig,
    ValidationValue,
};

#[derive(Debug, Clone, Default, PartialEq, Eq)]
/// Structured metadata describing configuration fields.
///
/// `ConfigMetadata` is the manual metadata API behind `tier`'s higher-level
/// derive support. It can describe:
///
/// - field-level behavior such as env names, aliases, secret paths, examples,
///   merge policies, and declared validation rules
/// - cross-field validation checks such as mutually exclusive or required-if
///   relationships
///
/// # Examples
///
/// ```
/// use tier::{ConfigMetadata, FieldMetadata};
///
/// let metadata = ConfigMetadata::from_fields([
///     FieldMetadata::new("db.url").env("DATABASE_URL"),
///     FieldMetadata::new("db.password").secret(),
/// ])
/// .required_with("tls.enabled", ["tls.cert", "tls.key"]);
///
/// assert_eq!(
///     metadata
///         .env_overrides()
///         .expect("valid metadata")
///         .get("DATABASE_URL")
///         .map(String::as_str),
///     Some("db.url")
/// );
/// assert_eq!(metadata.secret_paths(), vec!["db.password".to_owned()]);
/// assert_eq!(metadata.checks().len(), 1);
/// ```
pub struct ConfigMetadata {
    fields: Vec<FieldMetadata>,
    checks: Vec<ValidationCheck>,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct MetadataMatchScore {
    segment_count: usize,
    specificity: usize,
    positional_specificity: Vec<bool>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
/// Metadata for a single configuration path.
pub struct FieldMetadata {
    /// Dot-delimited configuration path.
    pub path: String,
    /// Alternate dot-delimited paths accepted by serde during deserialization.
    pub aliases: Vec<String>,
    /// Whether values at this path should be treated as sensitive.
    pub secret: bool,
    /// Exact environment variable name to map to this path.
    pub env: Option<String>,
    /// Decoder applied to environment variable values before deserialization.
    pub env_decode: Option<EnvDecoder>,
    /// Human-readable field documentation.
    pub doc: Option<String>,
    /// Example value rendered in generated docs.
    pub example: Option<String>,
    /// Deprecation note shown in generated docs and runtime warnings.
    pub deprecated: Option<String>,
    /// Whether the field accepts omission via `serde(default)`.
    pub has_default: bool,
    /// Strategy used when merging layered values into this field.
    pub merge: MergeStrategy,
    /// Whether the merge strategy was explicitly declared for this field.
    pub merge_explicit: bool,
    /// Source kinds allowed to override this field.
    ///
    /// When unset, the field accepts values from any source kind.
    pub allowed_sources: Option<BTreeSet<SourceKind>>,
    /// Source kinds explicitly denied from overriding this field.
    ///
    /// When unset, the field does not deny any source kinds.
    pub denied_sources: Option<BTreeSet<SourceKind>>,
    /// Declarative validation rules applied after normalization.
    pub validations: Vec<ValidationRule>,
    /// Per-rule configuration such as custom messages, warning levels, and tags.
    pub validation_configs: BTreeMap<String, ValidationRuleConfig>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct EffectiveSourcePolicy {
    pub(crate) allowed_sources: Option<BTreeSet<SourceKind>>,
    pub(crate) denied_sources: Option<BTreeSet<SourceKind>>,
}

pub(crate) struct EffectiveValidation {
    pub(crate) field: FieldMetadata,
    pub(crate) rule: ValidationRule,
}

/// Metadata produced for a configuration type.
pub trait TierMetadata {
    /// Returns metadata for the configuration type.
    #[must_use]
    fn metadata() -> ConfigMetadata {
        ConfigMetadata::default()
    }

    /// Returns configuration paths that should be treated as secrets.
    #[must_use]
    fn secret_paths() -> Vec<String> {
        Self::metadata().secret_paths()
    }
}