Skip to main content

consortium/
msg_tree.rs

1//! Message aggregation tree.
2//!
3//! Rust implementation of `ClusterShell.MsgTree`.
4//!
5//! The MsgTree is a message aggregation tree that groups identical output lines from
6//! multiple sources (nodes). It's memory-efficient -- identical message lines from
7//! different nodes share tree nodes.
8
9use std::collections::{HashMap, HashSet};
10
11use slotmap::{DefaultKey, SlotMap};
12
13/// Aggregates output lines into a tree structure for deduplication.
14#[derive(Debug)]
15pub struct MsgTree<K> {
16    arena: SlotMap<DefaultKey, MsgTreeNode<K>>,
17    root: DefaultKey,
18    current: HashMap<K, DefaultKey>, // key -> current node position
19    mode: MsgTreeMode,
20}
21
22/// Tree node stored in the slotmap arena
23#[derive(Debug)]
24struct MsgTreeNode<K> {
25    /// Child nodes keyed by message line bytes
26    children: HashMap<Vec<u8>, DefaultKey>,
27    /// Parent node key (None for root)
28    parent: Option<DefaultKey>,
29    /// Message line for this node (None for root)
30    msgline: Option<Vec<u8>>,
31    /// Keys associated with this node
32    keys: HashSet<K>,
33}
34
35impl<K> MsgTreeNode<K> {
36    fn new(parent: Option<DefaultKey>, msgline: Option<Vec<u8>>) -> Self {
37        MsgTreeNode {
38            children: HashMap::new(),
39            parent,
40            msgline,
41            keys: HashSet::new(),
42        }
43    }
44}
45
46/// MsgTree behavior modes
47#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
48pub enum MsgTreeMode {
49    /// Defer message processing until walk.
50    Defer = 0,
51    /// Process messages immediately.
52    Shift = 1,
53    /// Process messages immediately and keep backtrace of keys.
54    Trace = 2,
55}
56
57impl<K: Eq + std::hash::Hash + Clone> MsgTree<K> {
58    /// Create a new MsgTree with the given mode.
59    pub fn new(mode: MsgTreeMode) -> Self {
60        let mut arena: SlotMap<DefaultKey, MsgTreeNode<K>> = SlotMap::new();
61        let root_key = arena.insert(MsgTreeNode::new(None, None));
62        MsgTree {
63            arena,
64            root: root_key,
65            current: HashMap::new(),
66            mode,
67        }
68    }
69
70    /// Get the node from the arena by key
71    #[allow(dead_code)]
72    fn get_node(&self, key: DefaultKey) -> Option<&MsgTreeNode<K>> {
73        self.arena.get(key)
74    }
75
76    /// Get the node from the arena by key (mutable)
77    #[allow(dead_code)]
78    fn get_node_mut(&mut self, key: DefaultKey) -> Option<&mut MsgTreeNode<K>> {
79        self.arena.get_mut(key)
80    }
81
82    /// Add a message line for the given key.
83    pub fn add(&mut self, key: K, msgline: Vec<u8>) {
84        // Get current node for this key, or root if not tracked
85        let current_node = self.current.get(&key).copied().unwrap_or(self.root);
86
87        // Determine if we should do key shifting based on mode
88        let should_shift = self.mode >= MsgTreeMode::Shift;
89        let key_to_shift = if should_shift {
90            Some(key.clone())
91        } else {
92            None
93        };
94
95        // Walk from current node to find or create the child matching msgline
96        let new_node = self._append_to_node(current_node, msgline, key_to_shift);
97
98        // Update the key's current position
99        self.current.insert(key, new_node);
100    }
101
102    /// Append a message line to a node, creating child if needed and shifting keys
103    fn _append_to_node(
104        &mut self,
105        parent_key: DefaultKey,
106        msgline: Vec<u8>,
107        key_to_shift: Option<K>,
108    ) -> DefaultKey {
109        // Check if child with this msgline exists
110        {
111            let parent_node = self
112                .arena
113                .get_mut(parent_key)
114                .expect("parent node not found");
115            if let Some(&child_key) = parent_node.children.get(&msgline) {
116                // Child exists, return it
117                if let Some(k) = key_to_shift {
118                    self._shift_key(parent_key, child_key, k);
119                }
120                return child_key;
121            }
122        }
123
124        // Create new child node (borrow self.arena mutably without holding parent_node ref)
125        let child_key = self
126            .arena
127            .insert(MsgTreeNode::new(Some(parent_key), Some(msgline.clone())));
128
129        // Add child to parent (need to borrow again)
130        {
131            let parent_node = self
132                .arena
133                .get_mut(parent_key)
134                .expect("parent node not found");
135            parent_node.children.insert(msgline, child_key);
136        }
137
138        // If key shifting is enabled, shift the key
139        if let Some(k) = key_to_shift {
140            self._shift_key(parent_key, child_key, k);
141        }
142
143        child_key
144    }
145
146    /// Shift a key from parent to child (MODE_SHIFT behavior)
147    fn _shift_key(&mut self, parent_key: DefaultKey, child_key: DefaultKey, key: K) {
148        if let Some(parent_node) = self.arena.get_mut(parent_key) {
149            parent_node.keys.remove(&key);
150        }
151
152        if let Some(child_node) = self.arena.get_mut(child_key) {
153            child_node.keys.insert(key);
154        }
155    }
156
157    /// Update keys in MODE_DEFER - assigns keys to tree elements
158    fn _update_keys(&mut self) {
159        for (key, node_key) in self.current.iter() {
160            if let Some(node) = self.arena.get_mut(*node_key) {
161                node.keys.insert(key.clone());
162            }
163        }
164        // MODE_DEFER is no longer valid as keys are now assigned
165        self.mode = MsgTreeMode::Shift;
166    }
167
168    /// Return the number of keys contained in the MsgTree.
169    pub fn len(&self) -> usize {
170        self.current.len()
171    }
172
173    /// Returns true if the MsgTree contains no keys.
174    pub fn is_empty(&self) -> bool {
175        self.current.is_empty()
176    }
177
178    /// Remove all items from the MsgTree.
179    pub fn clear(&mut self) {
180        self.current.clear();
181        // Clear the arena and recreate root
182        self.arena.clear();
183        self.root = self.arena.insert(MsgTreeNode::new(None, None));
184    }
185
186    /// Return the message for key if key is in the MsgTree.
187    pub fn get(&self, key: &K) -> Option<MsgTreeElem<'_, K>> {
188        let node_key = self.current.get(key)?;
189        Some(MsgTreeElem {
190            tree: self,
191            node_key: *node_key,
192        })
193    }
194
195    /// Return an iterator over MsgTree's keys.
196    pub fn keys(&self) -> impl Iterator<Item = &K> {
197        self.current.keys()
198    }
199
200    /// Walk the tree and return an iterator over (message, keys) tuples.
201    pub fn walk<'a>(&'a mut self, match_filter: Option<&'a dyn Fn(&K) -> bool>) -> WalkIter<'a, K> {
202        // In Defer mode, update keys first
203        if self.mode == MsgTreeMode::Defer {
204            self._update_keys();
205        }
206
207        WalkIter::new(self, match_filter)
208    }
209
210    /// Walk the tree in trace mode. Only valid in Trace mode.
211    pub fn walk_trace<'a>(
212        &'a mut self,
213        match_filter: Option<&'a dyn Fn(&K) -> bool>,
214    ) -> WalkTraceIter<'a, K> {
215        assert_eq!(
216            self.mode,
217            MsgTreeMode::Trace,
218            "walk_trace() is only callable in trace mode"
219        );
220
221        WalkTraceIter::new(self, match_filter)
222    }
223
224    /// Remove keys matching the filter
225    pub fn remove(&mut self, match_filter: &dyn Fn(&K) -> bool) {
226        // Collect keys to remove
227        let keys_to_remove: Vec<K> = self
228            .current
229            .keys()
230            .filter(|k| match_filter(*k))
231            .cloned()
232            .collect();
233
234        // Remove from arena
235        for key in &keys_to_remove {
236            if let Some(&node_key) = self.current.get(key) {
237                if let Some(node) = self.arena.get_mut(node_key) {
238                    node.keys.remove(key);
239                }
240            }
241            self.current.remove(key);
242        }
243    }
244}
245
246/// A message element representing a path from a node to the root.
247#[derive(Debug)]
248pub struct MsgTreeElem<'a, K> {
249    tree: &'a MsgTree<K>,
250    node_key: DefaultKey,
251}
252
253impl<'a, K> MsgTreeElem<'a, K> {
254    /// Get the whole message buffer as bytes.
255    pub fn message(&self) -> Vec<u8> {
256        let mut lines: Vec<&[u8]> = Vec::new();
257        let mut current = Some(self.node_key);
258
259        while let Some(node_key) = current {
260            if let Some(node) = self.tree.arena.get(node_key) {
261                if let Some(ref msgline) = node.msgline {
262                    lines.push(msgline.as_slice());
263                }
264                current = node.parent;
265            } else {
266                break;
267            }
268        }
269
270        // Reverse to get root-to-leaf order
271        lines.reverse();
272
273        if lines.is_empty() {
274            Vec::new()
275        } else {
276            let mut result = lines[0].to_vec();
277            for line in &lines[1..] {
278                result.push(b'\n');
279                result.extend_from_slice(line);
280            }
281            result
282        }
283    }
284
285    /// Get the message lines as an iterator
286    pub fn lines(&self) -> LinesIter<'_, K> {
287        LinesIter {
288            tree: self.tree,
289            current: Some(self.node_key),
290        }
291    }
292}
293
294/// Iterator over message lines
295pub struct LinesIter<'a, K> {
296    tree: &'a MsgTree<K>,
297    current: Option<DefaultKey>,
298}
299
300impl<'a, K> Iterator for LinesIter<'a, K> {
301    type Item = &'a [u8];
302
303    fn next(&mut self) -> Option<Self::Item> {
304        let node_key = self.current?;
305        if let Some(node) = self.tree.arena.get(node_key) {
306            self.current = node.parent;
307            node.msgline.as_ref().map(|msg| msg.as_slice())
308        } else {
309            None
310        }
311    }
312}
313
314/// Iterator over walk results.
315pub struct WalkIter<'a, K> {
316    tree: &'a mut MsgTree<K>,
317    stack: Vec<DefaultKey>,
318    match_filter: Option<&'a dyn Fn(&K) -> bool>,
319}
320
321impl<'a, K> WalkIter<'a, K> {
322    fn new(tree: &'a mut MsgTree<K>, match_filter: Option<&'a dyn Fn(&K) -> bool>) -> Self {
323        let mut stack = Vec::new();
324        // Start with root's children (collect into Vec to enable rev())
325        if let Some(root_node) = tree.arena.get(tree.root) {
326            let mut children: Vec<DefaultKey> = root_node.children.values().copied().collect();
327            children.reverse();
328            stack.extend(children);
329        }
330        WalkIter {
331            tree,
332            stack,
333            match_filter,
334        }
335    }
336}
337
338impl<'a, K: Clone> Iterator for WalkIter<'a, K> {
339    type Item = (Vec<u8>, Vec<K>);
340
341    fn next(&mut self) -> Option<Self::Item> {
342        while let Some(node_key) = self.stack.pop() {
343            // Add children to stack
344            // Collect into Vec to enable rev()
345            if let Some(node) = self.tree.arena.get(node_key) {
346                let mut children: Vec<DefaultKey> = node.children.values().copied().collect();
347                children.reverse();
348                self.stack.extend(children);
349            }
350
351            // Check if this node has keys
352            if let Some(node) = self.tree.arena.get(node_key) {
353                if !node.keys.is_empty() {
354                    // Filter keys if needed
355                    let filtered_keys: Vec<K> = if let Some(filter) = &self.match_filter {
356                        node.keys.iter().filter(|k| filter(*k)).cloned().collect()
357                    } else {
358                        node.keys.iter().cloned().collect()
359                    };
360
361                    if !filtered_keys.is_empty() {
362                        // Build message for this node
363                        let mut lines: Vec<&[u8]> = Vec::new();
364                        let mut current = Some(node_key);
365
366                        while let Some(nk) = current {
367                            if let Some(n) = self.tree.arena.get(nk) {
368                                if let Some(ref msgline) = n.msgline {
369                                    lines.push(msgline.as_slice());
370                                }
371                                current = n.parent;
372                            } else {
373                                break;
374                            }
375                        }
376
377                        lines.reverse();
378
379                        let result = if lines.is_empty() {
380                            Vec::new()
381                        } else {
382                            let mut result = lines[0].to_vec();
383                            for line in &lines[1..] {
384                                result.push(b'\n');
385                                result.extend_from_slice(line);
386                            }
387                            result
388                        };
389
390                        return Some((result, filtered_keys));
391                    }
392                }
393            }
394        }
395        None
396    }
397}
398
399/// Iterator over walk_trace results.
400pub struct WalkTraceIter<'a, K> {
401    tree: &'a mut MsgTree<K>,
402    stack: Vec<(DefaultKey, usize)>,
403    match_filter: Option<&'a dyn Fn(&K) -> bool>,
404}
405
406impl<'a, K> WalkTraceIter<'a, K> {
407    fn new(tree: &'a mut MsgTree<K>, match_filter: Option<&'a dyn Fn(&K) -> bool>) -> Self {
408        let mut stack = Vec::new();
409        // Start with root's children at depth 1 (collect into Vec to enable rev())
410        if let Some(root_node) = tree.arena.get(tree.root) {
411            let mut children: Vec<(DefaultKey, usize)> =
412                root_node.children.iter().map(|(_, &k)| (k, 1)).collect();
413            children.reverse();
414            stack.extend(children);
415        }
416        WalkTraceIter {
417            tree,
418            stack,
419            match_filter,
420        }
421    }
422}
423
424impl<'a, K: Clone> Iterator for WalkTraceIter<'a, K> {
425    type Item = (Vec<u8>, Vec<K>, usize, usize);
426
427    fn next(&mut self) -> Option<Self::Item> {
428        while let Some((node_key, depth)) = self.stack.pop() {
429            // Add children to stack
430            // Collect into Vec to enable rev()
431            if let Some(node) = self.tree.arena.get(node_key) {
432                let mut children: Vec<(DefaultKey, usize)> =
433                    node.children.iter().map(|(_, &k)| (k, depth + 1)).collect();
434                children.reverse();
435                self.stack.extend(children);
436            }
437
438            // Check if this node has keys
439            if let Some(node) = self.tree.arena.get(node_key) {
440                if !node.keys.is_empty() {
441                    // Filter keys if needed
442                    let filtered_keys: Vec<K> = if let Some(filter) = &self.match_filter {
443                        node.keys.iter().filter(|k| filter(*k)).cloned().collect()
444                    } else {
445                        node.keys.iter().cloned().collect()
446                    };
447
448                    if !filtered_keys.is_empty() {
449                        // Get msgline for this node
450                        let msgline = node.msgline.clone().unwrap_or_default();
451
452                        // Count children
453                        let num_children = node.children.len();
454
455                        return Some((msgline, filtered_keys, depth, num_children));
456                    }
457                }
458            }
459        }
460        None
461    }
462}
463
464#[cfg(test)]
465mod tests {
466    use super::*;
467
468    #[test]
469    fn test_new_is_empty() {
470        let tree: MsgTree<String> = MsgTree::new(MsgTreeMode::Defer);
471        assert!(tree.is_empty());
472        assert_eq!(tree.len(), 0);
473    }
474
475    #[test]
476    fn test_mode_enum() {
477        assert_eq!(MsgTreeMode::Defer as u8, 0);
478        assert_eq!(MsgTreeMode::Shift as u8, 1);
479        assert_eq!(MsgTreeMode::Trace as u8, 2);
480    }
481
482    #[test]
483    fn test_add_single_key() {
484        let mut tree = MsgTree::new(MsgTreeMode::Defer);
485        tree.add("key1".to_string(), b"message".to_vec());
486        assert_eq!(tree.len(), 1);
487    }
488
489    #[test]
490    fn test_add_multiple_lines_same_key() {
491        let mut tree = MsgTree::new(MsgTreeMode::Defer);
492        tree.add("key1".to_string(), b"message1".to_vec());
493        tree.add("key1".to_string(), b"message2".to_vec());
494        assert_eq!(tree.len(), 1);
495
496        let elem = tree.get(&"key1".to_string()).unwrap();
497        assert_eq!(elem.message(), b"message1\nmessage2");
498    }
499
500    #[test]
501    fn test_add_multiple_keys_same_message() {
502        let mut tree = MsgTree::new(MsgTreeMode::Defer);
503        tree.add("key1".to_string(), b"message".to_vec());
504        tree.add("key2".to_string(), b"message".to_vec());
505        assert_eq!(tree.len(), 2);
506
507        let mut walk_results: Vec<_> = tree.walk(None).collect();
508        assert_eq!(walk_results.len(), 1);
509        let (msg, keys) = walk_results.pop().unwrap();
510        assert_eq!(msg, b"message");
511        assert_eq!(keys.len(), 2);
512    }
513
514    #[test]
515    fn test_walk_with_different_messages() {
516        let mut tree = MsgTree::new(MsgTreeMode::Defer);
517        tree.add("key1".to_string(), b"message0".to_vec());
518        tree.add("key2".to_string(), b"message2".to_vec());
519        tree.add("key3".to_string(), b"message3".to_vec());
520
521        let mut walk_results: Vec<_> = tree.walk(None).collect();
522        assert_eq!(walk_results.len(), 3);
523    }
524
525    #[test]
526    fn test_walk_with_match_filter() {
527        let mut tree: MsgTree<(&str, String)> = MsgTree::new(MsgTreeMode::Shift);
528        tree.add(("item1", "key1".to_string()), b"message0".to_vec());
529        tree.add(("item2", "key2".to_string()), b"message2".to_vec());
530        tree.add(("item3", "key3".to_string()), b"message3".to_vec());
531
532        let walk_results: Vec<_> = tree
533            .walk(Some(&|k: &(&str, String)| k.0 == "item2"))
534            .collect();
535        assert_eq!(walk_results.len(), 1);
536    }
537
538    #[test]
539    fn test_walk_with_mapper() {
540        let mut tree: MsgTree<(&str, String)> = MsgTree::new(MsgTreeMode::Shift);
541        tree.add(("item1", "key1".to_string()), b"message0".to_vec());
542        tree.add(("item2", "key2".to_string()), b"message2".to_vec());
543
544        let walk_results: Vec<_> = tree
545            .walk(Some(&|k: &(&str, String)| k.0.starts_with("item")))
546            .collect();
547        assert_eq!(walk_results.len(), 2);
548    }
549
550    #[test]
551    fn test_get_message() {
552        let mut tree = MsgTree::new(MsgTreeMode::Shift);
553        tree.add("key1".to_string(), b"message1".to_vec());
554        tree.add("key1".to_string(), b"message2".to_vec());
555
556        let elem = tree.get(&"key1".to_string()).unwrap();
557        assert_eq!(elem.message(), b"message1\nmessage2");
558    }
559
560    #[test]
561    fn test_get_nonexistent_key() {
562        let mut tree = MsgTree::new(MsgTreeMode::Shift);
563        tree.add("key1".to_string(), b"message".to_vec());
564
565        let elem = tree.get(&"nonexistent".to_string());
566        assert!(elem.is_none());
567    }
568
569    #[test]
570    fn test_clear() {
571        let mut tree = MsgTree::new(MsgTreeMode::Defer);
572        tree.add("key1".to_string(), b"message".to_vec());
573        tree.add("key2".to_string(), b"message".to_vec());
574
575        tree.clear();
576        assert!(tree.is_empty());
577        assert_eq!(tree.len(), 0);
578    }
579
580    #[test]
581    fn test_mode_defer_to_shift() {
582        let mut tree = MsgTree::new(MsgTreeMode::Defer);
583        tree.add("key1".to_string(), b"message0".to_vec());
584        tree.add("key2".to_string(), b"message1".to_vec());
585
586        assert_eq!(tree.mode, MsgTreeMode::Defer);
587
588        // Calling walk should trigger _update_keys and change mode
589        let _results: Vec<_> = tree.walk(None).collect();
590
591        assert_eq!(tree.mode, MsgTreeMode::Shift);
592    }
593
594    #[test]
595    fn test_trace_mode() {
596        let mut tree = MsgTree::new(MsgTreeMode::Trace);
597        tree.add("key1".to_string(), b"message0".to_vec());
598        tree.add("key2".to_string(), b"message2".to_vec());
599        tree.add("key3".to_string(), b"message2".to_vec());
600
601        // key2 and key3 share the same message, so walk returns 2 groups
602        let walk_results: Vec<_> = tree.walk(None).collect();
603        assert_eq!(walk_results.len(), 2);
604
605        // walk_trace should be available
606        let trace_results: Vec<_> = tree.walk_trace(None).collect();
607        assert_eq!(trace_results.len(), 2);
608    }
609
610    #[test]
611    #[should_panic(expected = "walk_trace() is only callable in trace mode")]
612    fn test_walk_trace_in_shift_mode_panics() {
613        let mut tree = MsgTree::new(MsgTreeMode::Shift);
614        tree.add("key1".to_string(), b"message".to_vec());
615        let _results: Vec<_> = tree.walk_trace(None).collect();
616    }
617
618    #[test]
619    fn test_remove_keys() {
620        let mut tree = MsgTree::new(MsgTreeMode::Shift);
621        tree.add("key1".to_string(), b"message0".to_vec());
622        tree.add("key2".to_string(), b"message0".to_vec());
623        tree.add("key3".to_string(), b"message0".to_vec());
624
625        tree.remove(&|k: &String| k == "key2");
626
627        assert_eq!(tree.len(), 2);
628        assert!(tree.get(&"key2".to_string()).is_none());
629    }
630
631    #[test]
632    fn test_deep_tree() {
633        let mut tree = MsgTree::new(MsgTreeMode::Shift);
634        for i in 0..100 {
635            tree.add("key1".to_string(), format!("line{}", i).as_bytes().to_vec());
636        }
637
638        let elem = tree.get(&"key1".to_string()).unwrap();
639        let msg = elem.message();
640        let lines: Vec<_> = msg.split(|&b| b == b'\n').collect();
641        assert_eq!(lines.len(), 100);
642    }
643
644    #[test]
645    fn test_shared_prefix() {
646        let mut tree = MsgTree::new(MsgTreeMode::Shift);
647
648        // Multiple keys with shared prefix
649        tree.add("key1".to_string(), b"prefix".to_vec());
650        tree.add("key1".to_string(), b"common".to_vec());
651        tree.add("key2".to_string(), b"prefix".to_vec());
652        tree.add("key2".to_string(), b"common".to_vec());
653
654        let elem = tree.get(&"key1".to_string()).unwrap();
655        assert_eq!(elem.message(), b"prefix\ncommon");
656
657        let elem = tree.get(&"key2".to_string()).unwrap();
658        assert_eq!(elem.message(), b"prefix\ncommon");
659    }
660
661    #[test]
662    fn test_keys_iterator() {
663        let mut tree = MsgTree::new(MsgTreeMode::Shift);
664        tree.add("key1".to_string(), b"message".to_vec());
665        tree.add("key2".to_string(), b"message".to_vec());
666        tree.add("key3".to_string(), b"message".to_vec());
667
668        let keys: Vec<_> = tree.keys().cloned().collect();
669        assert_eq!(keys.len(), 3);
670        assert!(keys.contains(&"key1".to_string()));
671        assert!(keys.contains(&"key2".to_string()));
672        assert!(keys.contains(&"key3".to_string()));
673    }
674}