rpn_cli/calc/
undo.rs

1use crate::core::stack::{StackItem, ValueStack};
2use std::fmt::{Display, Formatter};
3
4#[derive(Debug, PartialEq)]
5pub struct Undo {
6    remove: usize,
7    insert: Vec<StackItem>,
8    tokens: Vec<String>,
9}
10
11impl Undo {
12    pub fn new(remove: usize, insert: Vec<StackItem>, tokens: Vec<String>) -> Self {
13        Self { remove, insert, tokens }
14    }
15
16    pub fn is_empty(&self) -> bool {
17        self.remove == 0 && self.insert.is_empty()
18    }
19
20    pub fn merge(&mut self, remove: usize, mut insert: Vec<StackItem>, token: Option<&str>) {
21        if let Some(start) = insert.len().checked_sub(self.remove) {
22            insert.drain(start..);
23            insert.append(&mut self.insert);
24            self.remove = remove;
25            self.insert = insert;
26        } else if let Some(delta) = self.remove.checked_sub(insert.len()) {
27            self.remove = remove + delta;
28        }
29        if let Some(token) = token {
30            self.tokens.push(String::from(token));
31        }
32    }
33
34    pub fn add_tokens(&mut self, token: Option<&str>, mut tokens: Vec<String>) {
35        if let Some(token) = token {
36            self.tokens.push(String::from(token));
37        }
38        self.tokens.append(&mut tokens);
39    }
40
41    pub fn clear(&mut self) {
42        self.remove = 0;
43        self.insert.clear();
44        self.tokens.clear();
45    }
46
47    pub fn apply_to(&mut self, stack: &mut ValueStack) -> Self {
48        let start = stack.wrapped_len().checked_sub(self.remove).unwrap_or(0);
49        let remove = self.insert.len();
50        let insert = stack.wrapped_drain(start..).collect();
51        let tokens = self.tokens.drain(..).collect();
52        stack.wrapped_append(&mut self.insert);
53        self.remove = 0;
54        Self::new(remove, insert, tokens)
55    }
56}
57
58impl Default for Undo {
59    fn default() -> Self {
60        Self::new(0, Vec::new(), Vec::new())
61    }
62}
63
64impl Display for Undo {
65    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
66        let tokens = self.tokens.join(" ");
67        write!(f, "{}", tokens)?;
68        Ok(())
69    }
70}
71
72#[cfg(test)]
73pub mod tests {
74    use crate::calc::undo::Undo;
75    use crate::core::stack::tests::parse_items;
76    use crate::core::stack::ValueStack;
77    use pretty_assertions::assert_eq;
78
79    #[test]
80    fn test_values_are_merged_with_underflow() {
81        let mut undo = create_undo(1, vec!["2", "3"], vec![]);
82        undo.merge(1, parse_items(vec!["1", "5"]), None);
83        assert_eq!(undo.remove, 1);
84        assert_eq!(undo.insert, parse_items(vec!["1", "2", "3"]));
85    }
86
87    #[test]
88    fn test_values_are_merged_with_overflow() {
89        let mut undo = create_undo(3, vec!["2", "3"], vec![]);
90        undo.merge(1, parse_items(vec!["1", "5"]), None);
91        assert_eq!(undo.remove, 2);
92        assert_eq!(undo.insert, parse_items(vec!["2", "3"]));
93    }
94
95    #[test]
96    fn test_changes_are_applied_with_underflow() {
97        let mut stack = ValueStack::new(parse_items(vec!["1", "2", "3"]));
98        let mut undo = create_undo(2, vec!["4", "5"], vec![]);
99        undo.apply_to(&mut stack);
100        assert_eq!(stack.items, parse_items(vec!["1", "4", "5"]));
101    }
102
103    #[test]
104    fn test_changes_are_applied_with_overflow() {
105        let mut stack = ValueStack::new(parse_items(vec!["1", "2", "3"]));
106        let mut undo = create_undo(4, vec!["4", "5"], vec![]);
107        undo.apply_to(&mut stack);
108        assert_eq!(stack.items, parse_items(vec!["4", "5"]));
109    }
110
111    pub fn create_undo(remove: usize, insert: Vec<&str>, tokens: Vec<&str>) -> Undo {
112        let insert = parse_items(insert);
113        let tokens = tokens.iter().map(|x| x.to_string()).collect();
114        Undo::new(remove, insert, tokens)
115    }
116}