Skip to main content

structfs_core_store/
path_trie.rs

1//! A generic prefix trie keyed by path components.
2//!
3//! `PathTrie<T>` provides O(k) operations where k is the path depth.
4//! Each node can optionally hold a value, and has children indexed by path component.
5
6use crate::{path, Path};
7use std::collections::BTreeMap;
8
9/// A prefix trie keyed by path components.
10///
11/// Each node can optionally hold a value of type T, and has children
12/// indexed by path component strings. This provides O(k) operations
13/// where k is the path depth.
14///
15/// # Example
16///
17/// ```rust
18/// use structfs_core_store::{PathTrie, path};
19///
20/// let mut trie: PathTrie<i32> = PathTrie::new();
21/// trie.insert(&path!("a/b"), 1);
22/// trie.insert(&path!("a/b/c"), 2);
23///
24/// assert_eq!(trie.get(&path!("a/b")), Some(&1));
25///
26/// // find_ancestor returns the deepest value along the path
27/// let (value, suffix) = trie.find_ancestor(&path!("a/b/c/d")).unwrap();
28/// assert_eq!(*value, 2);
29/// assert_eq!(suffix, path!("d"));
30/// ```
31#[derive(Debug, Clone)]
32pub struct PathTrie<T> {
33    value: Option<T>,
34    children: BTreeMap<String, PathTrie<T>>,
35}
36
37impl<T> Default for PathTrie<T> {
38    fn default() -> Self {
39        Self {
40            value: None,
41            children: BTreeMap::new(),
42        }
43    }
44}
45
46impl<T> PathTrie<T> {
47    /// Create an empty trie.
48    pub fn new() -> Self {
49        Self::default()
50    }
51
52    /// Navigate to node, creating intermediate nodes as needed.
53    fn get_or_create_node(&mut self, path: &Path) -> &mut PathTrie<T> {
54        let mut current = self;
55        for component in path.iter() {
56            current = current.children.entry(component.to_string()).or_default();
57        }
58        current
59    }
60
61    /// Navigate to node if it exists.
62    fn get_node(&self, path: &Path) -> Option<&PathTrie<T>> {
63        let mut current = self;
64        for component in path.iter() {
65            current = current.children.get(component)?;
66        }
67        Some(current)
68    }
69
70    /// Navigate to node if it exists (mutable).
71    fn get_node_mut(&mut self, path: &Path) -> Option<&mut PathTrie<T>> {
72        let mut current = self;
73        for component in path.iter() {
74            current = current.children.get_mut(component)?;
75        }
76        Some(current)
77    }
78
79    /// Insert a value at path. Returns previous value if any.
80    pub fn insert(&mut self, path: &Path, value: T) -> Option<T> {
81        let node = self.get_or_create_node(path);
82        node.value.replace(value)
83    }
84
85    /// Remove and return value at exact path. Children remain.
86    pub fn remove(&mut self, path: &Path) -> Option<T> {
87        self.get_node_mut(path)?.value.take()
88    }
89
90    /// Remove and return entire subtree at path.
91    pub fn remove_subtree(&mut self, path: &Path) -> Option<PathTrie<T>> {
92        if path.is_empty() {
93            let old = std::mem::take(self);
94            if old.value.is_some() || !old.children.is_empty() {
95                Some(old)
96            } else {
97                None
98            }
99        } else {
100            let parent_path = path.slice(0, path.len() - 1);
101            let child_name = &path[path.len() - 1];
102            let parent = self.get_node_mut(&parent_path)?;
103            parent.children.remove(child_name)
104        }
105    }
106
107    /// Get reference to value at exact path.
108    pub fn get(&self, path: &Path) -> Option<&T> {
109        self.get_node(path)?.value.as_ref()
110    }
111
112    /// Get mutable reference to value at exact path.
113    pub fn get_mut(&mut self, path: &Path) -> Option<&mut T> {
114        self.get_node_mut(path)?.value.as_mut()
115    }
116
117    /// Get reference to subtrie at path.
118    pub fn get_subtrie(&self, path: &Path) -> Option<&PathTrie<T>> {
119        self.get_node(path)
120    }
121
122    /// Get mutable reference to subtrie at path.
123    pub fn get_subtrie_mut(&mut self, path: &Path) -> Option<&mut PathTrie<T>> {
124        self.get_node_mut(path)
125    }
126
127    /// Check if exact path has a value.
128    pub fn contains_value(&self, path: &Path) -> bool {
129        self.get(path).is_some()
130    }
131
132    /// Count of values in trie (not nodes).
133    pub fn len(&self) -> usize {
134        let self_count = if self.value.is_some() { 1 } else { 0 };
135        let children_count: usize = self.children.values().map(|child| child.len()).sum();
136        self_count + children_count
137    }
138
139    /// True if no values anywhere in trie.
140    pub fn is_empty(&self) -> bool {
141        self.value.is_none() && self.children.values().all(|c| c.is_empty())
142    }
143
144    /// Find deepest ancestor with a value.
145    /// Returns (value_ref, remaining_suffix).
146    pub fn find_ancestor(&self, path: &Path) -> Option<(&T, Path)> {
147        let mut current = self;
148        let mut last_value: Option<&T> = self.value.as_ref();
149        let mut last_depth: usize = 0;
150
151        for (depth, component) in path.iter().enumerate() {
152            match current.children.get(component) {
153                Some(child) => {
154                    current = child;
155                    if child.value.is_some() {
156                        last_value = child.value.as_ref();
157                        last_depth = depth + 1;
158                    }
159                }
160                None => break,
161            }
162        }
163
164        last_value.map(|v| {
165            let suffix = path.slice(last_depth, path.len());
166            (v, suffix)
167        })
168    }
169
170    /// Mutable version of find_ancestor.
171    /// Due to borrow checker constraints, this uses a two-pass approach.
172    pub fn find_ancestor_mut(&mut self, path: &Path) -> Option<(&mut T, Path)> {
173        // First pass: find the depth
174        let depth = {
175            let mut current = &*self;
176            let mut last_depth: usize = if self.value.is_some() { 0 } else { usize::MAX };
177
178            for (d, component) in path.iter().enumerate() {
179                match current.children.get(component) {
180                    Some(child) => {
181                        current = child;
182                        if child.value.is_some() {
183                            last_depth = d + 1;
184                        }
185                    }
186                    None => break,
187                }
188            }
189
190            if last_depth == usize::MAX {
191                return None;
192            }
193            last_depth
194        };
195
196        // Second pass: get mutable reference
197        let target_path = path.slice(0, depth);
198        let suffix = path.slice(depth, path.len());
199
200        self.get_mut(&target_path).map(|v| (v, suffix))
201    }
202
203    /// Iterate over all (path, value) pairs.
204    pub fn iter(&self) -> PathTrieIter<'_, T> {
205        PathTrieIter::new(self)
206    }
207}
208
209/// Iterator over (Path, &T) pairs in a PathTrie.
210pub struct PathTrieIter<'a, T> {
211    stack: Vec<(Path, &'a PathTrie<T>)>,
212}
213
214impl<'a, T> PathTrieIter<'a, T> {
215    fn new(trie: &'a PathTrie<T>) -> Self {
216        Self {
217            stack: vec![(path!(""), trie)],
218        }
219    }
220}
221
222impl<'a, T> Iterator for PathTrieIter<'a, T> {
223    type Item = (Path, &'a T);
224
225    fn next(&mut self) -> Option<Self::Item> {
226        while let Some((path, node)) = self.stack.pop() {
227            // Push children onto stack (in reverse order for correct iteration)
228            for (name, child) in node.children.iter().rev() {
229                // `name` is a trie key: a component of an already-validated
230                // path, so re-validation is unnecessary (debug-checked only).
231                let child_path = if path.is_empty() {
232                    Path::from_validated_components(vec![name.clone()])
233                } else {
234                    let mut c: Vec<String> = path.iter().map(str::to_string).collect();
235                    c.push(name.clone());
236                    Path::from_validated_components(c)
237                };
238                self.stack.push((child_path, child));
239            }
240
241            // Yield this node if it has a value
242            if let Some(ref value) = node.value {
243                return Some((path, value));
244            }
245        }
246        None
247    }
248}
249
250#[cfg(test)]
251mod tests {
252    use super::*;
253    use crate::path;
254
255    #[test]
256    fn new_trie_is_empty() {
257        let trie: PathTrie<i32> = PathTrie::new();
258        assert!(trie.is_empty());
259        assert_eq!(trie.len(), 0);
260    }
261
262    #[test]
263    fn insert_and_get() {
264        let mut trie: PathTrie<i32> = PathTrie::new();
265        trie.insert(&path!("a/b"), 42);
266
267        assert_eq!(trie.get(&path!("a/b")), Some(&42));
268        assert_eq!(trie.get(&path!("a")), None);
269        assert_eq!(trie.get(&path!("a/b/c")), None);
270    }
271
272    #[test]
273    fn insert_returns_previous() {
274        let mut trie: PathTrie<i32> = PathTrie::new();
275        assert_eq!(trie.insert(&path!("a"), 1), None);
276        assert_eq!(trie.insert(&path!("a"), 2), Some(1));
277        assert_eq!(trie.get(&path!("a")), Some(&2));
278    }
279
280    #[test]
281    fn remove_returns_value() {
282        let mut trie: PathTrie<i32> = PathTrie::new();
283        trie.insert(&path!("a/b"), 42);
284
285        assert_eq!(trie.remove(&path!("a/b")), Some(42));
286        assert_eq!(trie.get(&path!("a/b")), None);
287    }
288
289    #[test]
290    fn remove_keeps_children() {
291        let mut trie: PathTrie<i32> = PathTrie::new();
292        trie.insert(&path!("a"), 1);
293        trie.insert(&path!("a/b"), 2);
294
295        trie.remove(&path!("a"));
296
297        assert_eq!(trie.get(&path!("a")), None);
298        assert_eq!(trie.get(&path!("a/b")), Some(&2));
299    }
300
301    #[test]
302    fn remove_subtree() {
303        let mut trie: PathTrie<i32> = PathTrie::new();
304        trie.insert(&path!("a"), 1);
305        trie.insert(&path!("a/b"), 2);
306        trie.insert(&path!("c"), 3);
307
308        let subtree = trie.remove_subtree(&path!("a")).unwrap();
309
310        assert_eq!(subtree.get(&path!("")), Some(&1));
311        assert_eq!(subtree.get(&path!("b")), Some(&2));
312        assert_eq!(trie.get(&path!("a")), None);
313        assert_eq!(trie.get(&path!("a/b")), None);
314        assert_eq!(trie.get(&path!("c")), Some(&3));
315    }
316
317    #[test]
318    fn remove_subtree_at_root() {
319        let mut trie: PathTrie<i32> = PathTrie::new();
320        trie.insert(&path!("a"), 1);
321        trie.insert(&path!("b"), 2);
322
323        let subtree = trie.remove_subtree(&path!("")).unwrap();
324
325        assert!(trie.is_empty());
326        assert_eq!(subtree.get(&path!("a")), Some(&1));
327        assert_eq!(subtree.get(&path!("b")), Some(&2));
328    }
329
330    #[test]
331    fn remove_subtree_nonexistent() {
332        let mut trie: PathTrie<i32> = PathTrie::new();
333        trie.insert(&path!("a"), 1);
334
335        assert!(trie.remove_subtree(&path!("nonexistent")).is_none());
336    }
337
338    #[test]
339    fn get_mut() {
340        let mut trie: PathTrie<i32> = PathTrie::new();
341        trie.insert(&path!("a"), 1);
342
343        *trie.get_mut(&path!("a")).unwrap() = 42;
344
345        assert_eq!(trie.get(&path!("a")), Some(&42));
346    }
347
348    #[test]
349    fn contains_value() {
350        let mut trie: PathTrie<i32> = PathTrie::new();
351        trie.insert(&path!("a/b"), 1);
352
353        assert!(trie.contains_value(&path!("a/b")));
354        assert!(!trie.contains_value(&path!("a")));
355        assert!(!trie.contains_value(&path!("nonexistent")));
356    }
357
358    #[test]
359    fn len_and_is_empty() {
360        let mut trie: PathTrie<i32> = PathTrie::new();
361        assert!(trie.is_empty());
362        assert_eq!(trie.len(), 0);
363
364        trie.insert(&path!("a"), 1);
365        assert!(!trie.is_empty());
366        assert_eq!(trie.len(), 1);
367
368        trie.insert(&path!("a/b"), 2);
369        assert_eq!(trie.len(), 2);
370
371        trie.insert(&path!("c"), 3);
372        assert_eq!(trie.len(), 3);
373    }
374
375    #[test]
376    fn find_ancestor_basic() {
377        let mut trie: PathTrie<&str> = PathTrie::new();
378        trie.insert(&path!("data"), "data_store");
379
380        let (value, suffix) = trie.find_ancestor(&path!("data/users/1")).unwrap();
381        assert_eq!(*value, "data_store");
382        assert_eq!(suffix, path!("users/1"));
383    }
384
385    #[test]
386    fn find_ancestor_deeper_wins() {
387        let mut trie: PathTrie<&str> = PathTrie::new();
388        trie.insert(&path!("data"), "data_store");
389        trie.insert(&path!("data/cache"), "cache_store");
390
391        // Path that matches cache
392        let (value, suffix) = trie.find_ancestor(&path!("data/cache/hot")).unwrap();
393        assert_eq!(*value, "cache_store");
394        assert_eq!(suffix, path!("hot"));
395
396        // Path that only matches data
397        let (value, suffix) = trie.find_ancestor(&path!("data/users/1")).unwrap();
398        assert_eq!(*value, "data_store");
399        assert_eq!(suffix, path!("users/1"));
400    }
401
402    #[test]
403    fn find_ancestor_at_root() {
404        let mut trie: PathTrie<&str> = PathTrie::new();
405        trie.insert(&path!(""), "root_store");
406
407        let (value, suffix) = trie.find_ancestor(&path!("any/path")).unwrap();
408        assert_eq!(*value, "root_store");
409        assert_eq!(suffix, path!("any/path"));
410    }
411
412    #[test]
413    fn find_ancestor_no_match() {
414        let mut trie: PathTrie<&str> = PathTrie::new();
415        trie.insert(&path!("data"), "data_store");
416
417        assert!(trie.find_ancestor(&path!("other/path")).is_none());
418    }
419
420    #[test]
421    fn find_ancestor_exact_match() {
422        let mut trie: PathTrie<&str> = PathTrie::new();
423        trie.insert(&path!("data"), "data_store");
424
425        let (value, suffix) = trie.find_ancestor(&path!("data")).unwrap();
426        assert_eq!(*value, "data_store");
427        assert!(suffix.is_empty());
428    }
429
430    #[test]
431    fn find_ancestor_mut() {
432        let mut trie: PathTrie<i32> = PathTrie::new();
433        trie.insert(&path!("a"), 1);
434        trie.insert(&path!("a/b"), 2);
435
436        let (value, suffix) = trie.find_ancestor_mut(&path!("a/b/c")).unwrap();
437        assert_eq!(*value, 2);
438        assert_eq!(suffix, path!("c"));
439
440        // Mutate
441        *value = 42;
442        assert_eq!(trie.get(&path!("a/b")), Some(&42));
443    }
444
445    #[test]
446    fn find_ancestor_mut_no_match() {
447        let mut trie: PathTrie<i32> = PathTrie::new();
448        trie.insert(&path!("a"), 1);
449
450        assert!(trie.find_ancestor_mut(&path!("b/c")).is_none());
451    }
452
453    #[test]
454    fn iter_all_values() {
455        let mut trie: PathTrie<i32> = PathTrie::new();
456        trie.insert(&path!("a"), 1);
457        trie.insert(&path!("b"), 2);
458        trie.insert(&path!("a/c"), 3);
459
460        let mut items: Vec<_> = trie.iter().collect();
461        items.sort_by_key(|a| a.0.to_string());
462
463        assert_eq!(items.len(), 3);
464        assert_eq!(items[0], (path!("a"), &1));
465        assert_eq!(items[1], (path!("a/c"), &3));
466        assert_eq!(items[2], (path!("b"), &2));
467    }
468
469    #[test]
470    fn iter_empty() {
471        let trie: PathTrie<i32> = PathTrie::new();
472        assert_eq!(trie.iter().count(), 0);
473    }
474
475    #[test]
476    fn iter_root_value() {
477        let mut trie: PathTrie<i32> = PathTrie::new();
478        trie.insert(&path!(""), 42);
479
480        let items: Vec<_> = trie.iter().collect();
481        assert_eq!(items.len(), 1);
482        assert_eq!(items[0], (path!(""), &42));
483    }
484
485    #[test]
486    fn get_subtrie() {
487        let mut trie: PathTrie<i32> = PathTrie::new();
488        trie.insert(&path!("a/b"), 1);
489        trie.insert(&path!("a/c"), 2);
490
491        let subtrie = trie.get_subtrie(&path!("a")).unwrap();
492        assert_eq!(subtrie.get(&path!("b")), Some(&1));
493        assert_eq!(subtrie.get(&path!("c")), Some(&2));
494    }
495
496    #[test]
497    fn get_subtrie_nonexistent() {
498        let trie: PathTrie<i32> = PathTrie::new();
499        assert!(trie.get_subtrie(&path!("nonexistent")).is_none());
500    }
501
502    #[test]
503    fn clone_trie() {
504        let mut trie: PathTrie<i32> = PathTrie::new();
505        trie.insert(&path!("a"), 1);
506
507        let cloned = trie.clone();
508        assert_eq!(cloned.get(&path!("a")), Some(&1));
509    }
510
511    #[test]
512    fn default_trie() {
513        let trie: PathTrie<i32> = PathTrie::default();
514        assert!(trie.is_empty());
515    }
516
517    #[test]
518    fn debug_trie() {
519        let mut trie: PathTrie<i32> = PathTrie::new();
520        trie.insert(&path!("a"), 1);
521        let debug = format!("{:?}", trie);
522        assert!(debug.contains("PathTrie"));
523    }
524
525    #[test]
526    fn insert_at_root() {
527        let mut trie: PathTrie<i32> = PathTrie::new();
528        trie.insert(&path!(""), 42);
529
530        assert_eq!(trie.get(&path!("")), Some(&42));
531        assert_eq!(trie.len(), 1);
532    }
533
534    #[test]
535    fn remove_at_root() {
536        let mut trie: PathTrie<i32> = PathTrie::new();
537        trie.insert(&path!(""), 42);
538
539        assert_eq!(trie.remove(&path!("")), Some(42));
540        assert!(trie.is_empty());
541    }
542
543    #[test]
544    fn get_subtrie_mut() {
545        let mut trie: PathTrie<i32> = PathTrie::new();
546        trie.insert(&path!("a/b"), 1);
547
548        let subtrie = trie.get_subtrie_mut(&path!("a")).unwrap();
549        subtrie.insert(&path!("c"), 2);
550
551        assert_eq!(trie.get(&path!("a/c")), Some(&2));
552    }
553
554    #[test]
555    fn remove_subtree_empty_trie() {
556        let mut trie: PathTrie<i32> = PathTrie::new();
557        assert!(trie.remove_subtree(&path!("")).is_none());
558    }
559
560    #[test]
561    fn is_empty_with_only_structure() {
562        let mut trie: PathTrie<i32> = PathTrie::new();
563        trie.insert(&path!("a/b/c"), 1);
564        trie.remove(&path!("a/b/c"));
565
566        // Trie has structure but no values
567        assert!(trie.is_empty());
568    }
569}