tier 0.1.14

Rust configuration library for layered TOML, env, and CLI settings
Documentation
use serde::{Deserialize, Serialize};
use tier::{ConfigLoader, ConfigMetadata, EnvSource, FieldMetadata, Secret};

#[derive(Debug, Clone, Serialize, Deserialize)]
struct AppConfig {
    db: DbConfig,
    tls: TlsConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct DbConfig {
    url: String,
    password: Secret<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct TlsConfig {
    enabled: bool,
    cert: Option<String>,
    key: Option<String>,
}

impl Default for AppConfig {
    fn default() -> Self {
        Self {
            db: DbConfig {
                url: "postgres://localhost/app".to_owned(),
                password: Secret::new("default-secret".to_owned()),
            },
            tls: TlsConfig {
                enabled: true,
                cert: Some("/etc/tier/tls.crt".to_owned()),
                key: Some("/etc/tier/tls.key".to_owned()),
            },
        }
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let metadata = ConfigMetadata::from_fields([
        FieldMetadata::new("db.url")
            .env("DATABASE_URL")
            .doc("Primary database connection string"),
        FieldMetadata::new("db.password").secret().non_empty(),
        FieldMetadata::new("tls.cert").absolute_path(),
        FieldMetadata::new("tls.key").absolute_path(),
    ])
    .required_if("tls.enabled", true, ["tls.cert", "tls.key"]);

    let env = EnvSource::from_pairs([
        ("DATABASE_URL", "\"postgres://env/app\""),
        ("APP__DB__PASSWORD", "\"rotated-secret\""),
    ])
    .prefix("APP");

    let loaded = ConfigLoader::new(AppConfig::default())
        .metadata(metadata)
        .env(env)
        .load()?;

    println!("{}", loaded.report().redacted_pretty_json());
    println!(
        "{}",
        loaded
            .report()
            .explain("db.password")
            .expect("db.password explanation")
    );
    Ok(())
}