Skip to main content

oxihuman_core/
chain_map.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5use std::collections::HashMap;
6
7/// A chain of maps where lookup falls through parent layers.
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct ChainMap {
11    layers: Vec<HashMap<String, String>>,
12}
13
14impl Default for ChainMap {
15    fn default() -> Self {
16        Self::new()
17    }
18}
19
20#[allow(dead_code)]
21impl ChainMap {
22    pub fn new() -> Self {
23        Self {
24            layers: vec![HashMap::new()],
25        }
26    }
27
28    pub fn push_layer(&mut self) {
29        self.layers.push(HashMap::new());
30    }
31
32    pub fn pop_layer(&mut self) -> bool {
33        if self.layers.len() > 1 {
34            self.layers.pop();
35            true
36        } else {
37            false
38        }
39    }
40
41    pub fn layer_count(&self) -> usize {
42        self.layers.len()
43    }
44
45    pub fn set(&mut self, key: &str, value: &str) {
46        if let Some(top) = self.layers.last_mut() {
47            top.insert(key.to_string(), value.to_string());
48        }
49    }
50
51    pub fn get(&self, key: &str) -> Option<&str> {
52        for layer in self.layers.iter().rev() {
53            if let Some(v) = layer.get(key) {
54                return Some(v.as_str());
55            }
56        }
57        None
58    }
59
60    pub fn contains_key(&self, key: &str) -> bool {
61        self.get(key).is_some()
62    }
63
64    pub fn remove_from_top(&mut self, key: &str) -> bool {
65        self.layers
66            .last_mut()
67            .is_some_and(|top| top.remove(key).is_some())
68    }
69
70    pub fn top_layer_count(&self) -> usize {
71        self.layers.last().map_or(0, |l| l.len())
72    }
73
74    pub fn total_entries(&self) -> usize {
75        self.layers.iter().map(|l| l.len()).sum()
76    }
77
78    pub fn all_keys(&self) -> Vec<String> {
79        let mut keys: Vec<String> = self.layers.iter().flat_map(|l| l.keys().cloned()).collect();
80        keys.sort();
81        keys.dedup();
82        keys
83    }
84
85    pub fn flatten(&self) -> HashMap<String, String> {
86        let mut result = HashMap::new();
87        for layer in &self.layers {
88            for (k, v) in layer {
89                result.insert(k.clone(), v.clone());
90            }
91        }
92        result
93    }
94
95    pub fn clear_top(&mut self) {
96        if let Some(top) = self.layers.last_mut() {
97            top.clear();
98        }
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_new() {
108        let cm = ChainMap::new();
109        assert_eq!(cm.layer_count(), 1);
110    }
111
112    #[test]
113    fn test_set_and_get() {
114        let mut cm = ChainMap::new();
115        cm.set("k", "v");
116        assert_eq!(cm.get("k"), Some("v"));
117    }
118
119    #[test]
120    fn test_layer_override() {
121        let mut cm = ChainMap::new();
122        cm.set("k", "base");
123        cm.push_layer();
124        cm.set("k", "override");
125        assert_eq!(cm.get("k"), Some("override"));
126    }
127
128    #[test]
129    fn test_fallthrough() {
130        let mut cm = ChainMap::new();
131        cm.set("k", "base");
132        cm.push_layer();
133        assert_eq!(cm.get("k"), Some("base"));
134    }
135
136    #[test]
137    fn test_pop_layer() {
138        let mut cm = ChainMap::new();
139        cm.set("k", "base");
140        cm.push_layer();
141        cm.set("k", "top");
142        assert!(cm.pop_layer());
143        assert_eq!(cm.get("k"), Some("base"));
144    }
145
146    #[test]
147    fn test_cannot_pop_last() {
148        let mut cm = ChainMap::new();
149        assert!(!cm.pop_layer());
150    }
151
152    #[test]
153    fn test_contains_key() {
154        let mut cm = ChainMap::new();
155        assert!(!cm.contains_key("x"));
156        cm.set("x", "y");
157        assert!(cm.contains_key("x"));
158    }
159
160    #[test]
161    fn test_all_keys() {
162        let mut cm = ChainMap::new();
163        cm.set("b", "1");
164        cm.push_layer();
165        cm.set("a", "2");
166        let keys = cm.all_keys();
167        assert_eq!(keys, vec!["a", "b"]);
168    }
169
170    #[test]
171    fn test_flatten() {
172        let mut cm = ChainMap::new();
173        cm.set("a", "1");
174        cm.push_layer();
175        cm.set("a", "2");
176        cm.set("b", "3");
177        let flat = cm.flatten();
178        assert_eq!(flat["a"], "2");
179        assert_eq!(flat["b"], "3");
180    }
181
182    #[test]
183    fn test_remove_from_top() {
184        let mut cm = ChainMap::new();
185        cm.set("a", "1");
186        cm.push_layer();
187        cm.set("a", "2");
188        assert!(cm.remove_from_top("a"));
189        assert_eq!(cm.get("a"), Some("1"));
190    }
191}