oxihuman_core/
persistent_map.rs1#![allow(dead_code)]
4
5use std::collections::HashMap;
6
7#[allow(dead_code)]
9pub struct PersistentMap {
10 current: HashMap<String, String>,
11 snapshots: Vec<HashMap<String, String>>,
12 version: u64,
13}
14
15#[allow(dead_code)]
16impl PersistentMap {
17 pub fn new() -> Self {
18 Self {
19 current: HashMap::new(),
20 snapshots: Vec::new(),
21 version: 0,
22 }
23 }
24 pub fn insert(&mut self, key: &str, value: &str) {
25 self.current.insert(key.to_string(), value.to_string());
26 self.version += 1;
27 }
28 pub fn get(&self, key: &str) -> Option<&str> {
29 self.current.get(key).map(|s| s.as_str())
30 }
31 pub fn remove(&mut self, key: &str) -> bool {
32 let removed = self.current.remove(key).is_some();
33 if removed {
34 self.version += 1;
35 }
36 removed
37 }
38 pub fn contains(&self, key: &str) -> bool {
39 self.current.contains_key(key)
40 }
41 pub fn snapshot(&mut self) {
42 self.snapshots.push(self.current.clone());
43 }
44 pub fn restore(&mut self, idx: usize) -> bool {
45 if let Some(snap) = self.snapshots.get(idx) {
46 self.current = snap.clone();
47 self.version += 1;
48 true
49 } else {
50 false
51 }
52 }
53 pub fn snapshot_count(&self) -> usize {
54 self.snapshots.len()
55 }
56 pub fn version(&self) -> u64 {
57 self.version
58 }
59 pub fn len(&self) -> usize {
60 self.current.len()
61 }
62 pub fn is_empty(&self) -> bool {
63 self.current.is_empty()
64 }
65 pub fn clear(&mut self) {
66 self.current.clear();
67 self.version += 1;
68 }
69 pub fn keys(&self) -> Vec<&str> {
70 self.current.keys().map(|s| s.as_str()).collect()
71 }
72 pub fn diff_from_snapshot(&self, idx: usize) -> Vec<String> {
73 if let Some(snap) = self.snapshots.get(idx) {
74 self.current
75 .keys()
76 .filter(|k| snap.get(*k) != self.current.get(*k))
77 .cloned()
78 .collect()
79 } else {
80 Vec::new()
81 }
82 }
83}
84
85impl Default for PersistentMap {
86 fn default() -> Self {
87 Self::new()
88 }
89}
90
91#[allow(dead_code)]
92pub fn new_persistent_map() -> PersistentMap {
93 PersistentMap::new()
94}
95#[allow(dead_code)]
96pub fn pm_insert(m: &mut PersistentMap, k: &str, v: &str) {
97 m.insert(k, v);
98}
99#[allow(dead_code)]
100pub fn pm_get<'a>(m: &'a PersistentMap, k: &str) -> Option<&'a str> {
101 m.get(k)
102}
103#[allow(dead_code)]
104pub fn pm_remove(m: &mut PersistentMap, k: &str) -> bool {
105 m.remove(k)
106}
107#[allow(dead_code)]
108pub fn pm_contains(m: &PersistentMap, k: &str) -> bool {
109 m.contains(k)
110}
111#[allow(dead_code)]
112pub fn pm_snapshot(m: &mut PersistentMap) {
113 m.snapshot();
114}
115#[allow(dead_code)]
116pub fn pm_restore(m: &mut PersistentMap, idx: usize) -> bool {
117 m.restore(idx)
118}
119#[allow(dead_code)]
120pub fn pm_snapshot_count(m: &PersistentMap) -> usize {
121 m.snapshot_count()
122}
123#[allow(dead_code)]
124pub fn pm_version(m: &PersistentMap) -> u64 {
125 m.version()
126}
127#[allow(dead_code)]
128pub fn pm_len(m: &PersistentMap) -> usize {
129 m.len()
130}
131#[allow(dead_code)]
132pub fn pm_is_empty(m: &PersistentMap) -> bool {
133 m.is_empty()
134}
135#[allow(dead_code)]
136pub fn pm_clear(m: &mut PersistentMap) {
137 m.clear();
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 #[test]
144 fn test_insert_get() {
145 let mut m = new_persistent_map();
146 pm_insert(&mut m, "a", "1");
147 assert_eq!(pm_get(&m, "a"), Some("1"));
148 }
149 #[test]
150 fn test_remove() {
151 let mut m = new_persistent_map();
152 pm_insert(&mut m, "x", "v");
153 assert!(pm_remove(&mut m, "x"));
154 assert!(!pm_contains(&m, "x"));
155 }
156 #[test]
157 fn test_snapshot_restore() {
158 let mut m = new_persistent_map();
159 pm_insert(&mut m, "k", "before");
160 pm_snapshot(&mut m);
161 pm_insert(&mut m, "k", "after");
162 pm_restore(&mut m, 0);
163 assert_eq!(pm_get(&m, "k"), Some("before"));
164 }
165 #[test]
166 fn test_snapshot_count() {
167 let mut m = new_persistent_map();
168 pm_snapshot(&mut m);
169 pm_snapshot(&mut m);
170 assert_eq!(pm_snapshot_count(&m), 2);
171 }
172 #[test]
173 fn test_version_increments() {
174 let mut m = new_persistent_map();
175 let v0 = pm_version(&m);
176 pm_insert(&mut m, "a", "1");
177 assert!(pm_version(&m) > v0);
178 }
179 #[test]
180 fn test_len() {
181 let mut m = new_persistent_map();
182 pm_insert(&mut m, "a", "1");
183 pm_insert(&mut m, "b", "2");
184 assert_eq!(pm_len(&m), 2);
185 }
186 #[test]
187 fn test_is_empty() {
188 let m = new_persistent_map();
189 assert!(pm_is_empty(&m));
190 }
191 #[test]
192 fn test_clear() {
193 let mut m = new_persistent_map();
194 pm_insert(&mut m, "a", "1");
195 pm_clear(&mut m);
196 assert!(pm_is_empty(&m));
197 }
198 #[test]
199 fn test_diff_from_snapshot() {
200 let mut m = new_persistent_map();
201 pm_insert(&mut m, "k", "old");
202 pm_snapshot(&mut m);
203 pm_insert(&mut m, "k", "new");
204 let diff = m.diff_from_snapshot(0);
205 assert!(diff.contains(&"k".to_string()));
206 }
207 #[test]
208 fn test_restore_invalid_idx() {
209 let mut m = new_persistent_map();
210 assert!(!pm_restore(&mut m, 99));
211 }
212}