use cfgmatic_source::{
CascadeLayer, CascadeLoader, CascadeScope, Format, Loader, MemorySource, MergeStrategy,
};
use serde::Deserialize;
use std::fs;
use tempfile::tempdir;
#[derive(Debug, Deserialize, PartialEq)]
struct AppConfig {
server: ServerConfig,
}
#[derive(Debug, Deserialize, PartialEq)]
struct ServerConfig {
host: String,
port: u16,
}
#[test]
fn test_loader_load_as_from_memory_source() {
let source = MemorySource::from_json(serde_json::json!({
"server": {
"host": "localhost",
"port": 8080
}
}));
let loader = Loader::new();
let config: AppConfig = loader
.load_as(&source)
.expect("memory source should deserialize");
assert_eq!(
config,
AppConfig {
server: ServerConfig {
host: "localhost".to_string(),
port: 8080,
},
}
);
}
#[test]
fn test_loader_merge_honors_shallow_strategy() {
let loader = Loader::builder()
.merge_strategy(MergeStrategy::Shallow)
.build();
let merged = loader
.merge(vec![
cfgmatic_source::ParsedContent::from_json(serde_json::json!({
"server": {
"host": "localhost",
"port": 8080
}
})),
cfgmatic_source::ParsedContent::from_json(serde_json::json!({
"server": {
"port": 3000
}
})),
])
.expect("shallow merge should succeed");
let server = merged.get("server").expect("server section should exist");
assert_eq!(
server
.get("port")
.and_then(cfgmatic_source::ParsedContent::as_integer),
Some(3000)
);
assert!(server.get("host").is_none());
}
#[test]
fn test_cascade_loader_applies_git_like_layering() {
let temp_dir = tempdir().expect("tempdir should be created");
let system = temp_dir.path().join("system.toml");
let global = temp_dir.path().join("global.toml");
let local = temp_dir.path().join("local.toml");
fs::write(
&system,
r#"
[server]
host = "system.example"
port = 8080
"#,
)
.expect("system layer should be written");
fs::write(
&global,
r"
[server]
port = 9000
",
)
.expect("global layer should be written");
fs::write(
&local,
r#"
[server]
host = "repo.example"
"#,
)
.expect("local layer should be written");
let loader = CascadeLoader::builder()
.add_layer(CascadeLayer::new(CascadeScope::System, &system).priority(10))
.add_layer(CascadeLayer::new(CascadeScope::Global, &global).priority(20))
.add_layer(CascadeLayer::new(CascadeScope::Local, &local).priority(30))
.default_format(Format::Toml)
.build();
let config: AppConfig = loader.load_as().expect("cascade should deserialize");
assert_eq!(
config,
AppConfig {
server: ServerConfig {
host: "repo.example".to_string(),
port: 9000,
},
}
);
}