1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//! The engine without the attribute.
//!
//! ```text
//! cargo run -p dynamic-config --example no_macro --features json
//! ```
//!
//! `load`, `LoadSpec`, `Layer` and `ConfigCell` are the whole of it; the macro
//! writes about twenty-five lines of glue over them. Reach for this when the
//! sources are decided at run time, or when a config type is built by another
//! macro that will not compose with this one.
use dynamic_config::{load, ConfigCell, Format, Layer, LoadSpec, Source};
use serde::Deserialize;
#[derive(Debug, Deserialize)]
// Read through `Debug`, which dead-code analysis does not count.
#[allow(dead_code)]
struct ServerConfig {
host: String,
port: u16,
}
// The same storage the macro emits: `const`, so it lives in a `static`.
static SERVER: ConfigCell<ServerConfig> = ConfigCell::new();
// And the same runtime layers.
static OVERRIDES: Layer = Layer::new();
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Sources can be anything by now — a path computed from an argument, a
// document just read off a socket. `Source` borrows, so nothing leaks.
let fetched = String::from(r#"{"server": {"host": "0.0.0.0"}}"#);
let sources = [
Source::file("dynamic-config/examples/config.json", Format::Json),
Source::inline(&fetched, Format::Json),
];
let spec = LoadSpec::new("server", &sources)
.with_env("APP_")
.with_overrides(&OVERRIDES);
SERVER.store(load::<ServerConfig>(&spec)?);
println!("{:?}", SERVER.load().unwrap());
// The same lock-free read the macro's `current()` performs.
let held = SERVER.load().expect("just stored");
OVERRIDES.set("port", 9999u16)?;
SERVER.store(load::<ServerConfig>(&spec)?);
println!("{:?}", SERVER.load().unwrap());
println!(
"\nthe Arc taken before the swap still reads {} — a reader keeps its \
own generation",
held.port
);
Ok(())
}