oxihuman_core/
config_layer.rs1#![allow(dead_code)]
4
5use std::collections::HashMap;
8
9#[allow(dead_code)]
11#[derive(Debug, Clone, Default)]
12pub struct ConfigLayer {
13 entries: HashMap<String, String>,
14 name: String,
15}
16
17#[allow(dead_code)]
18impl ConfigLayer {
19 pub fn new(name: &str) -> Self {
20 Self {
21 entries: HashMap::new(),
22 name: name.to_string(),
23 }
24 }
25
26 pub fn name(&self) -> &str {
27 &self.name
28 }
29
30 pub fn set(&mut self, key: &str, value: &str) {
31 self.entries.insert(key.to_string(), value.to_string());
32 }
33
34 pub fn get(&self, key: &str) -> Option<&str> {
35 self.entries.get(key).map(|v| v.as_str())
36 }
37
38 pub fn remove(&mut self, key: &str) -> bool {
39 self.entries.remove(key).is_some()
40 }
41
42 pub fn contains(&self, key: &str) -> bool {
43 self.entries.contains_key(key)
44 }
45
46 pub fn count(&self) -> usize {
47 self.entries.len()
48 }
49
50 pub fn is_empty(&self) -> bool {
51 self.entries.is_empty()
52 }
53
54 pub fn keys(&self) -> Vec<&str> {
55 self.entries.keys().map(|k| k.as_str()).collect()
56 }
57
58 pub fn clear(&mut self) {
59 self.entries.clear();
60 }
61}
62
63#[allow(dead_code)]
65#[derive(Debug, Clone, Default)]
66pub struct LayeredConfig {
67 layers: Vec<ConfigLayer>,
68}
69
70#[allow(dead_code)]
71impl LayeredConfig {
72 pub fn new() -> Self {
73 Self::default()
74 }
75
76 pub fn push_layer(&mut self, layer: ConfigLayer) {
77 self.layers.push(layer);
78 }
79
80 pub fn layer_count(&self) -> usize {
81 self.layers.len()
82 }
83
84 pub fn get(&self, key: &str) -> Option<&str> {
86 self.layers.iter().rev().find_map(|l| l.get(key))
87 }
88
89 pub fn get_or<'a>(&'a self, key: &str, default: &'a str) -> &'a str {
90 self.get(key).unwrap_or(default)
91 }
92
93 pub fn has_key(&self, key: &str) -> bool {
94 self.get(key).is_some()
95 }
96
97 pub fn all_keys(&self) -> Vec<String> {
99 let mut keys: Vec<String> = self
100 .layers
101 .iter()
102 .flat_map(|l| l.keys().into_iter().map(|s| s.to_string()))
103 .collect();
104 keys.sort();
105 keys.dedup();
106 keys
107 }
108
109 pub fn pop_layer(&mut self) -> Option<ConfigLayer> {
110 self.layers.pop()
111 }
112
113 pub fn clear(&mut self) {
114 self.layers.clear();
115 }
116
117 pub fn flatten(&self) -> HashMap<String, String> {
119 let mut out = HashMap::new();
120 for layer in &self.layers {
121 for key in layer.keys() {
122 if let Some(v) = layer.get(key) {
123 out.insert(key.to_string(), v.to_string());
124 }
125 }
126 }
127 out
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn layer_set_get() {
137 let mut l = ConfigLayer::new("base");
138 l.set("color", "red");
139 assert_eq!(l.get("color"), Some("red"));
140 }
141
142 #[test]
143 fn layer_name() {
144 let l = ConfigLayer::new("user");
145 assert_eq!(l.name(), "user");
146 }
147
148 #[test]
149 fn layer_remove() {
150 let mut l = ConfigLayer::new("test");
151 l.set("k", "v");
152 assert!(l.remove("k"));
153 assert!(!l.contains("k"));
154 }
155
156 #[test]
157 fn layered_resolution_last_wins() {
158 let mut base = ConfigLayer::new("base");
159 base.set("size", "10");
160 let mut user = ConfigLayer::new("user");
161 user.set("size", "20");
162
163 let mut lc = LayeredConfig::new();
164 lc.push_layer(base);
165 lc.push_layer(user);
166 assert_eq!(lc.get("size"), Some("20"));
167 }
168
169 #[test]
170 fn layered_fallback_to_base() {
171 let mut base = ConfigLayer::new("base");
172 base.set("mode", "auto");
173 let user = ConfigLayer::new("user");
174
175 let mut lc = LayeredConfig::new();
176 lc.push_layer(base);
177 lc.push_layer(user);
178 assert_eq!(lc.get("mode"), Some("auto"));
179 }
180
181 #[test]
182 fn get_or_default() {
183 let lc = LayeredConfig::new();
184 assert_eq!(lc.get_or("missing", "default"), "default");
185 }
186
187 #[test]
188 fn all_keys_deduped() {
189 let mut base = ConfigLayer::new("base");
190 base.set("a", "1");
191 base.set("b", "2");
192 let mut user = ConfigLayer::new("user");
193 user.set("b", "3");
194 user.set("c", "4");
195
196 let mut lc = LayeredConfig::new();
197 lc.push_layer(base);
198 lc.push_layer(user);
199 let keys = lc.all_keys();
200 assert_eq!(keys, vec!["a", "b", "c"]);
201 }
202
203 #[test]
204 fn flatten_resolves_correctly() {
205 let mut base = ConfigLayer::new("base");
206 base.set("x", "1");
207 let mut user = ConfigLayer::new("user");
208 user.set("x", "2");
209 user.set("y", "3");
210
211 let mut lc = LayeredConfig::new();
212 lc.push_layer(base);
213 lc.push_layer(user);
214 let flat = lc.flatten();
215 assert_eq!(flat.get("x").expect("should succeed"), "2");
216 assert_eq!(flat.get("y").expect("should succeed"), "3");
217 }
218
219 #[test]
220 fn pop_layer() {
221 let mut lc = LayeredConfig::new();
222 lc.push_layer(ConfigLayer::new("a"));
223 lc.push_layer(ConfigLayer::new("b"));
224 let popped = lc.pop_layer().expect("should succeed");
225 assert_eq!(popped.name(), "b");
226 assert_eq!(lc.layer_count(), 1);
227 }
228
229 #[test]
230 fn clear_all_layers() {
231 let mut lc = LayeredConfig::new();
232 lc.push_layer(ConfigLayer::new("x"));
233 lc.clear();
234 assert_eq!(lc.layer_count(), 0);
235 }
236}