Skip to main content

chryso_core/
system_params.rs

1use parking_lot::RwLock;
2use std::collections::HashMap;
3use std::sync::Arc;
4
5const DEFAULT_TENANT: &str = "__default__";
6
7#[derive(Debug, Clone, PartialEq)]
8pub enum SystemParamValue {
9    Float(f64),
10    Int(i64),
11    Bool(bool),
12    String(String),
13}
14
15impl SystemParamValue {
16    pub fn as_f64(&self) -> Option<f64> {
17        match self {
18            SystemParamValue::Float(value) => Some(*value),
19            SystemParamValue::Int(value) => Some(*value as f64),
20            _ => None,
21        }
22    }
23}
24
25#[derive(Debug, Default)]
26pub struct SystemParamRegistry {
27    tenants: RwLock<HashMap<String, HashMap<String, SystemParamValue>>>,
28}
29
30impl SystemParamRegistry {
31    pub fn new() -> Self {
32        Self {
33            tenants: RwLock::new(HashMap::new()),
34        }
35    }
36
37    pub fn shared() -> Arc<Self> {
38        Arc::new(Self::new())
39    }
40
41    pub fn set_param(&self, tenant: &str, key: impl Into<String>, value: SystemParamValue) {
42        let mut guard = self.tenants.write();
43        let entry = guard.entry(tenant.to_string()).or_default();
44        entry.insert(key.into(), value);
45    }
46
47    pub fn set_default_param(&self, key: impl Into<String>, value: SystemParamValue) {
48        self.set_param(DEFAULT_TENANT, key, value);
49    }
50
51    pub fn get_param(&self, tenant: Option<&str>, key: &str) -> Option<SystemParamValue> {
52        let guard = self.tenants.read();
53        if let Some(tenant) = tenant {
54            if let Some(params) = guard.get(tenant) {
55                if let Some(value) = params.get(key) {
56                    return Some(value.clone());
57                }
58            }
59        }
60        guard
61            .get(DEFAULT_TENANT)
62            .and_then(|params| params.get(key))
63            .cloned()
64    }
65
66    pub fn get_f64(&self, tenant: Option<&str>, key: &str) -> Option<f64> {
67        self.get_param(tenant, key).and_then(|value| value.as_f64())
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::{SystemParamRegistry, SystemParamValue};
74
75    #[test]
76    fn registry_uses_default_fallback() {
77        let registry = SystemParamRegistry::new();
78        registry.set_default_param("optimizer.cost.scan", SystemParamValue::Float(2.0));
79        assert_eq!(
80            registry.get_f64(Some("tenant-a"), "optimizer.cost.scan"),
81            Some(2.0)
82        );
83    }
84
85    #[test]
86    fn registry_prefers_tenant_specific_value() {
87        let registry = SystemParamRegistry::new();
88        registry.set_default_param("optimizer.cost.scan", SystemParamValue::Float(2.0));
89        registry.set_param(
90            "tenant-a",
91            "optimizer.cost.scan",
92            SystemParamValue::Float(3.0),
93        );
94        assert_eq!(
95            registry.get_f64(Some("tenant-a"), "optimizer.cost.scan"),
96            Some(3.0)
97        );
98    }
99}