use std::collections::HashMap;
use serde_json::Value;
pub type DeferredValue = Box<dyn Fn(&HashMap<String, Value>) -> Value + Send + Sync>;
pub fn resolve_deferred(config: &mut HashMap<String, Value>, deferred: &HashMap<String, DeferredValue>) {
let snapshot: HashMap<String, Value> = config.clone();
for (key, resolver) in deferred {
let resolved = resolver(&snapshot);
config.insert(key.clone(), resolved);
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_resolve_basic_deferred() {
let mut config: HashMap<String, Value> = HashMap::new();
config.insert("HOST".to_string(), json!("localhost"));
config.insert("PORT".to_string(), json!(5432));
let mut deferred: HashMap<String, DeferredValue> = HashMap::new();
deferred.insert(
"FULL_URL".to_string(),
Box::new(|config| {
let host = config["HOST"].as_str().unwrap_or("unknown");
let port = config["PORT"].as_u64().unwrap_or(0);
json!(format!("{}:{}", host, port))
}),
);
resolve_deferred(&mut config, &deferred);
assert_eq!(config["FULL_URL"], json!("localhost:5432"));
assert_eq!(config["HOST"], json!("localhost"));
assert_eq!(config["PORT"], json!(5432));
}
#[test]
fn test_multiple_deferred_see_snapshot() {
let mut config: HashMap<String, Value> = HashMap::new();
config.insert("BASE".to_string(), json!("hello"));
let mut deferred: HashMap<String, DeferredValue> = HashMap::new();
deferred.insert(
"A".to_string(),
Box::new(|config| {
let base = config["BASE"].as_str().unwrap_or("");
json!(format!("{}-a", base))
}),
);
deferred.insert(
"B".to_string(),
Box::new(|config| {
let has_a = config.contains_key("A");
json!(has_a)
}),
);
resolve_deferred(&mut config, &deferred);
assert_eq!(config["A"], json!("hello-a"));
assert_eq!(config["B"], json!(false));
}
#[test]
fn test_deferred_after_merge() {
let mut config: HashMap<String, Value> = HashMap::new();
config.insert("ENV".to_string(), json!("production"));
config.insert("HOST".to_string(), json!("prod.example.com"));
let mut deferred: HashMap<String, DeferredValue> = HashMap::new();
deferred.insert(
"API_URL".to_string(),
Box::new(|config| {
let host = config["HOST"].as_str().unwrap_or("localhost");
let env = config["ENV"].as_str().unwrap_or("dev");
json!(format!("https://{}/api/{}", host, env))
}),
);
resolve_deferred(&mut config, &deferred);
assert_eq!(config["API_URL"], json!("https://prod.example.com/api/production"));
}
#[test]
fn test_empty_deferred() {
let mut config: HashMap<String, Value> = HashMap::new();
config.insert("KEY".to_string(), json!("value"));
let deferred: HashMap<String, DeferredValue> = HashMap::new();
resolve_deferred(&mut config, &deferred);
assert_eq!(config["KEY"], json!("value"));
}
}