1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use std::fmt::{Display, Formatter};

use crate::value::Value;

#[derive(Debug, PartialEq)]
pub struct Undo {
    remove: usize,
    insert: Vec<Value>,
    tokens: Vec<String>,
}

impl Undo {
    pub fn new(remove: usize, insert: Vec<Value>, tokens: Vec<String>) -> Undo {
        Self { remove, insert, tokens }
    }

    pub fn is_empty(&self) -> bool {
        self.remove == 0 && self.insert.is_empty()
    }

    pub fn merge(&mut self, remove: usize, mut insert: Vec<Value>, token: Option<&str>) {
        if let Some(start) = insert.len().checked_sub(self.remove) {
            insert.drain(start..);
            insert.append(&mut self.insert);
            self.remove = remove;
            self.insert = insert;
        } else if let Some(delta) = self.remove.checked_sub(insert.len()) {
            self.remove = remove + delta;
        }
        if let Some(token) = token {
            self.tokens.push(String::from(token));
        }
    }

    pub fn clear(&mut self) {
        self.remove = 0;
        self.insert.clear();
    }

    pub fn apply_to(&mut self, stack: &mut Vec<Value>) -> Undo {
        let start = stack.len().checked_sub(self.remove).unwrap_or(0);
        let remove = self.insert.len();
        let insert = stack.drain(start..).collect();
        let tokens = self.tokens.drain(..).collect();
        stack.append(&mut self.insert);
        self.remove = 0;
        return Self::new(remove, insert, tokens);
    }
}

impl Default for Undo {
    fn default() -> Undo {
        Self::new(0, Vec::new(), Vec::new())
    }
}

impl Display for Undo {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let tokens = self.tokens.join(" ");
        write!(f, "{}", tokens)?;
        return Ok(());
    }
}

#[cfg(test)]
pub mod tests {
    use pretty_assertions::assert_eq;

    use crate::engine::tests::create_values;
    use crate::undo::Undo;

    #[test]
    fn test_values_are_merged_with_underflow() {
        let mut undo = create_undo(1, vec!["2", "3"], vec![]);
        undo.merge(1, create_values(vec!["1", "5"]), None);
        assert_eq!(undo.remove, 1);
        assert_eq!(undo.insert, create_values(vec!["1", "2", "3"]));
    }

    #[test]
    fn test_values_are_merged_with_overflow() {
        let mut undo = create_undo(3, vec!["2", "3"], vec![]);
        undo.merge(1, create_values(vec!["1", "5"]), None);
        assert_eq!(undo.remove, 2);
        assert_eq!(undo.insert, create_values(vec!["2", "3"]));
    }

    #[test]
    fn test_changes_are_applied_with_underflow() {
        let mut stack = create_values(vec!["1", "2", "3"]);
        let mut undo = create_undo(2, vec!["4", "5"], vec![]);
        undo.apply_to(&mut stack);
        assert_eq!(stack, create_values(vec!["1", "4", "5"]));
    }

    #[test]
    fn test_changes_are_applied_with_overflow() {
        let mut stack = create_values(vec!["1", "2", "3"]);
        let mut undo = create_undo(4, vec!["4", "5"], vec![]);
        undo.apply_to(&mut stack);
        assert_eq!(stack, create_values(vec!["4", "5"]));
    }

    pub fn create_undo(remove: usize, insert: Vec<&str>, tokens: Vec<&str>) -> Undo {
        let insert = create_values(insert);
        let tokens = tokens.iter().map(|x| x.to_string()).collect();
        return Undo::new(remove, insert, tokens);
    }
}