use premortem::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, DeriveValidate)]
struct AppConfig {
#[serde(default = "default_host")]
host: String,
#[serde(default = "default_port")]
port: u16,
#[serde(default)]
debug: bool,
#[serde(default = "default_log_level")]
log_level: String,
}
fn default_host() -> String {
"localhost".to_string()
}
fn default_port() -> u16 {
8080
}
fn default_log_level() -> String {
"info".to_string()
}
impl Default for AppConfig {
fn default() -> Self {
Self {
host: default_host(),
port: default_port(),
debug: false,
log_level: default_log_level(),
}
}
}
fn main() {
println!("=== Value Tracing Demo ===\n");
let traced = Config::<AppConfig>::builder()
.source(Defaults::from(AppConfig::default()))
.source(Toml::file("config.toml").optional())
.source(Env::prefix("APP_"))
.build_traced()
.unwrap_or_else(|errors| {
eprintln!("Configuration errors:");
for error in errors.iter() {
eprintln!(" - {}", error);
}
std::process::exit(1);
});
println!("Configuration loaded successfully!\n");
println!("Final Configuration:");
println!(" host: {}", traced.host);
println!(" port: {}", traced.port);
println!(" debug: {}", traced.debug);
println!(" log_level: {}", traced.log_level);
println!();
let overridden: Vec<&str> = traced.overridden_paths().collect();
if overridden.is_empty() {
println!("No values were overridden (all from defaults)");
} else {
println!("Overridden paths ({}):", overridden.len());
for path in &overridden {
println!(" - {}", path);
}
}
println!();
println!("=== Detailed Traces ===\n");
for path in &["host", "port", "debug", "log_level"] {
if let Some(trace) = traced.trace(path) {
println!("{}:", path);
println!(
" Final value: {:?} (from {})",
trace.final_value.value, trace.final_value.source
);
if trace.was_overridden() {
println!(" Override history:");
for entry in &trace.history {
let marker = if entry.is_final { "→" } else { " " };
let note = if !entry.is_final {
" (overridden)"
} else {
" (final)"
};
println!(
" {} [{}] {:?}{}",
marker, entry.source, entry.value, note
);
}
}
println!();
}
}
println!("=== Full Trace Report ===\n");
println!("{}", traced.trace_report());
let config = traced.into_inner();
println!("Ready to use config: {:?}", config);
}