#[path = "support.rs"]
mod support;
use figment::providers::{Env, Format, Serialized};
use figment::Figment;
use noyalib::figment::Yaml;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
struct AppConfig {
name: String,
port: u16,
log_level: String,
workers: u32,
}
impl Default for AppConfig {
fn default() -> Self {
Self {
name: "noyalib-app".into(),
port: 8080,
log_level: "info".into(),
workers: 1,
}
}
}
const SITE_YAML: &str = "\
name: production-api
port: 9090
log_level: warn
";
fn main() {
support::header("Figment — layered YAML / env / defaults");
support::task_with_output("Layer 1 — figment with defaults only", || {
let cfg: AppConfig = Figment::new()
.merge(Serialized::defaults(AppConfig::default()))
.extract()
.expect("defaults must extract");
vec![
format!("name = {}", cfg.name),
format!("port = {}", cfg.port),
format!("log_level = {}", cfg.log_level),
format!("workers = {}", cfg.workers),
]
});
support::task_with_output("Layer 2 — defaults overlaid by YAML", || {
let cfg: AppConfig = Figment::new()
.merge(Serialized::defaults(AppConfig::default()))
.merge(Yaml::string(SITE_YAML))
.extract()
.expect("yaml overlay must extract");
vec![
format!("name = {} (from YAML)", cfg.name),
format!("port = {} (from YAML)", cfg.port),
format!("log_level = {} (from YAML)", cfg.log_level),
format!("workers = {} (still default)", cfg.workers),
]
});
support::task_with_output("Layer 3 — env vars override the YAML layer", || {
std::env::set_var("NOYAEX_PORT", "7000");
std::env::set_var("NOYAEX_WORKERS", "8");
let cfg: AppConfig = Figment::new()
.merge(Serialized::defaults(AppConfig::default()))
.merge(Yaml::string(SITE_YAML))
.merge(Env::prefixed("NOYAEX_"))
.extract()
.expect("env overlay must extract");
std::env::remove_var("NOYAEX_PORT");
std::env::remove_var("NOYAEX_WORKERS");
vec![
format!("name = {} (from YAML)", cfg.name),
format!("port = {} (from env: NOYAEX_PORT)", cfg.port),
format!("log_level = {} (from YAML)", cfg.log_level),
format!(
"workers = {} (from env: NOYAEX_WORKERS)",
cfg.workers
),
]
});
support::task_with_output(
"Pattern — production overrides staging overrides base",
|| {
const BASE: &str = "name: api\nport: 8080\nlog_level: debug\nworkers: 1\n";
const STAGING: &str = "log_level: info\nworkers: 4\n";
const PROD: &str = "port: 443\nlog_level: warn\nworkers: 16\n";
let cfg: AppConfig = Figment::new()
.merge(Yaml::string(BASE))
.merge(Yaml::string(STAGING))
.merge(Yaml::string(PROD))
.extract()
.expect("multi-file overlay must extract");
vec![
format!("name = {} (base)", cfg.name),
format!("port = {} (prod)", cfg.port),
format!("log_level = {} (prod)", cfg.log_level),
format!("workers = {} (prod)", cfg.workers),
]
},
);
println!();
println!(" Figment lets you compose configuration the way 12-factor");
println!(" apps demand: defaults in code, environment-specific YAML");
println!(" overlays, env-var overrides at deploy time. noyalib slots");
println!(" in as the YAML provider — no `serde_yaml` 0.9 dependency.");
support::footer();
}