use dynamic_config::{load, Error, ErrorKind, Format, LoadSpec, Origin, Source};
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Db {
#[allow(dead_code)]
host: String,
#[allow(dead_code)]
port: u16,
}
fn attempt(label: &str, sources: &[Source<'_>]) {
println!("── {label}");
match load::<Db>(&LoadSpec::new("db", sources)) {
Ok(config) => println!(" loaded: {config:?}\n"),
Err(error) => {
describe(&error);
println!();
}
}
}
fn describe(error: &Error) {
let advice = match error.kind() {
ErrorKind::Io => "check the file's permissions",
ErrorKind::Parse => "somebody saved a broken file",
ErrorKind::Missing => "the deployment is incomplete",
ErrorKind::Type => "the value is there but the wrong shape",
ErrorKind::Env => "an environment variable could not be read",
ErrorKind::Invalid => "it parsed, but the configuration is nonsense",
ErrorKind::Backend => "the loader itself could not proceed",
_ => "unclassified",
};
println!(" kind: {:?} — {advice}", error.kind());
if !error.path().is_empty() {
println!(" path: {}", error.path());
}
match error.origin() {
Origin::File(path) => println!(" origin: the file {}", path.display()),
Origin::Env(prefix) => println!(" origin: the environment, under {prefix}"),
Origin::Runtime(layer) => println!(" origin: set from code as a {layer}"),
Origin::Inline => println!(" origin: a source compiled into the binary"),
_ => println!(" origin: unknown"),
}
println!(" log: {error}");
}
fn main() {
attempt(
"a complete configuration",
&[Source::inline(
r#"{"db": {"host": "localhost", "port": 5432}}"#,
Format::Json,
)],
);
attempt(
"a file nobody can parse",
&[Source::inline("{ not json", Format::Json)],
);
attempt(
"a key nothing supplies",
&[Source::inline(r#"{"db": {"host": "a"}}"#, Format::Json)],
);
attempt(
"a value of the wrong shape",
&[Source::inline(
r#"{"db": {"host": "a", "port": "not-a-number"}}"#,
Format::Json,
)],
);
attempt(
"a missing file, plus one that is there",
&[
Source::file("does/not/exist.json", Format::Json),
Source::inline(r#"{"db": {"host": "a", "port": 1}}"#, Format::Json),
],
);
println!("note: a missing file is skipped, not an error — which is what");
println!(" makes an optional `secrets.json` work.");
}