use serde::Serialize;
use serde_json::{Map, Value};
use std::sync::OnceLock;
static GLOBAL_CONFIG: OnceLock<std::sync::Mutex<Map<String, Value>>> = OnceLock::new();
pub fn init_config() {
GLOBAL_CONFIG.get_or_init(|| std::sync::Mutex::new(Map::new()));
}
pub fn register_config<T: Serialize>(namespace: &str, config: T) {
let global = GLOBAL_CONFIG.get_or_init(|| std::sync::Mutex::new(Map::new()));
let value = serde_json::to_value(config).expect("Serialization failed");
let mut guard = global.lock().expect("Mutex poisoned");
guard.insert(namespace.to_string(), value);
}
pub fn get_config() -> Value {
if let Some(global) = GLOBAL_CONFIG.get() {
let guard = global.lock().expect("Mutex poisoned");
Value::Object(guard.clone())
} else {
Value::Object(Map::new())
}
}
pub fn get_config_by_key(key: &str) -> Option<Value> {
let mut current = get_config();
for part in key.split('.') {
match current {
Value::Object(map) => {
if let Some(v) = map.get(part) {
current = v.clone();
} else {
return None;
}
}
_ => return None,
}
}
Some(current)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[derive(serde::Serialize)]
struct DummyConfig {
port: u16,
host: String,
}
#[test]
fn test_register_and_get_full_config() {
init_config();
register_config(
"server",
DummyConfig {
port: 3000,
host: "0.0.0.0".into(),
},
);
register_config(
"logging",
json!({
"level": "info",
"file": "app.log",
}),
);
let config = get_config();
if let Value::Object(map) = config {
assert!(map.contains_key("server"));
assert!(map.contains_key("logging"));
} else {
panic!("Global config is not an object");
}
}
#[test]
fn test_get_config_by_key() {
init_config();
register_config(
"server",
DummyConfig {
port: 3000,
host: "0.0.0.0".into(),
},
);
let port = get_config_by_key("server.port");
assert_eq!(port, Some(json!(3000)));
let host = get_config_by_key("server.host");
assert_eq!(host, Some(json!("0.0.0.0")));
let missing = get_config_by_key("server.nonexistent");
assert!(missing.is_none());
}
}