Skip to main content

rab/tui/
undo_stack.rs

1/// Generic undo stack for storing state snapshots.
2///
3/// Stores clones of state snapshots. Popped snapshots are returned
4/// directly since they are already detached.
5#[derive(Debug, Clone, Default)]
6pub struct UndoStack<S> {
7    stack: Vec<S>,
8}
9
10impl<S: Clone> UndoStack<S> {
11    pub fn new() -> Self {
12        Self { stack: Vec::new() }
13    }
14
15    /// Push a clone of the given state onto the stack.
16    pub fn push(&mut self, state: &S) {
17        self.stack.push(state.clone());
18    }
19
20    /// Pop and return the most recent snapshot, or None if empty.
21    pub fn pop(&mut self) -> Option<S> {
22        self.stack.pop()
23    }
24
25    /// Remove all snapshots.
26    pub fn clear(&mut self) {
27        self.stack.clear();
28    }
29
30    pub fn len(&self) -> usize {
31        self.stack.len()
32    }
33
34    pub fn is_empty(&self) -> bool {
35        self.stack.is_empty()
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    #[test]
44    fn test_push_and_pop() {
45        let mut stack = UndoStack::new();
46        stack.push(&"hello".to_string());
47        stack.push(&"world".to_string());
48        assert_eq!(stack.len(), 2);
49        assert_eq!(stack.pop(), Some("world".to_string()));
50        assert_eq!(stack.pop(), Some("hello".to_string()));
51        assert_eq!(stack.pop(), None);
52    }
53
54    #[test]
55    fn test_clear() {
56        let mut stack = UndoStack::new();
57        stack.push(&1);
58        stack.push(&2);
59        assert_eq!(stack.len(), 2);
60        stack.clear();
61        assert!(stack.is_empty());
62    }
63
64    #[test]
65    fn test_clone_semantics() {
66        let mut stack = UndoStack::new();
67        let original = "data".to_string();
68        stack.push(&original);
69        // Modify original - popped value should be unchanged
70        drop(original);
71        assert_eq!(stack.pop(), Some("data".to_string()));
72    }
73}