Skip to main content

merman_core/config/
mod.rs

1use serde_json::{Map, Value};
2use std::sync::Arc;
3
4#[derive(Debug, Clone, PartialEq)]
5pub struct MermaidConfig(Arc<Value>);
6
7impl Default for MermaidConfig {
8    fn default() -> Self {
9        Self::empty_object()
10    }
11}
12
13impl MermaidConfig {
14    pub fn empty_object() -> Self {
15        Self(Arc::new(Value::Object(Map::new())))
16    }
17
18    pub fn from_value(value: Value) -> Self {
19        Self(Arc::new(value))
20    }
21
22    pub fn as_value(&self) -> &Value {
23        self.0.as_ref()
24    }
25
26    pub fn as_value_mut(&mut self) -> &mut Value {
27        Arc::make_mut(&mut self.0)
28    }
29
30    pub fn get_str(&self, dotted_path: &str) -> Option<&str> {
31        let mut cur: &Value = self.0.as_ref();
32        for segment in dotted_path.split('.') {
33            cur = cur.as_object()?.get(segment)?;
34        }
35        cur.as_str()
36    }
37
38    pub fn get_bool(&self, dotted_path: &str) -> Option<bool> {
39        let mut cur: &Value = self.0.as_ref();
40        for segment in dotted_path.split('.') {
41            cur = cur.as_object()?.get(segment)?;
42        }
43        cur.as_bool()
44    }
45
46    pub fn set_value(&mut self, dotted_path: &str, value: Value) {
47        let root_value = Arc::make_mut(&mut self.0);
48        // Be defensive: callers can construct `MermaidConfig` from any JSON value via
49        // `from_value`. Mermaid configs are objects; if we see a non-object here, coerce it
50        // to an object so this API never panics on user input.
51        if !root_value.is_object() {
52            *root_value = Value::Object(Map::new());
53        }
54
55        let Value::Object(root) = root_value else {
56            return;
57        };
58        let mut cur: &mut Map<String, Value> = root;
59        let mut segments = dotted_path.split('.').peekable();
60        while let Some(seg) = segments.next() {
61            if segments.peek().is_none() {
62                cur.insert(seg.to_string(), value);
63                return;
64            }
65            let slot = cur.entry(seg).or_insert_with(|| Value::Object(Map::new()));
66            if !slot.is_object() {
67                *slot = Value::Object(Map::new());
68            }
69            let Some(next) = slot.as_object_mut() else {
70                return;
71            };
72            cur = next;
73        }
74    }
75
76    pub fn deep_merge(&mut self, other: &Value) {
77        let Value::Object(m) = other else {
78            let base = Arc::make_mut(&mut self.0);
79            deep_merge_value(base, other);
80            return;
81        };
82        if m.is_empty() {
83            return;
84        }
85        let base = Arc::make_mut(&mut self.0);
86        deep_merge_value(base, other);
87    }
88}
89
90fn deep_merge_value(base: &mut Value, incoming: &Value) {
91    match (base, incoming) {
92        (Value::Object(base_map), Value::Object(in_map)) => {
93            for (key, in_value) in in_map {
94                match base_map.get_mut(key) {
95                    Some(base_value) => deep_merge_value(base_value, in_value),
96                    None => {
97                        base_map.insert(key.clone(), in_value.clone());
98                    }
99                }
100            }
101        }
102        (base_slot, in_value) => {
103            *base_slot = in_value.clone();
104        }
105    }
106}