diff options
author | Akshay <[email protected]> | 2021-03-12 14:54:55 +0000 |
---|---|---|
committer | Akshay <[email protected]> | 2021-03-12 14:54:55 +0000 |
commit | 9f0dc65dabff2a1e443199ed68292398dcb390b1 (patch) | |
tree | 17b29a4315df3d5caaa967b079c3d459063c0de9 /src/undo.rs |
init
Diffstat (limited to 'src/undo.rs')
-rw-r--r-- | src/undo.rs | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/src/undo.rs b/src/undo.rs new file mode 100644 index 0000000..2249fe7 --- /dev/null +++ b/src/undo.rs | |||
@@ -0,0 +1,133 @@ | |||
1 | #[derive(Copy, Clone, Debug)] | ||
2 | pub struct ModifyRecord { | ||
3 | pub point: (i32, i32), | ||
4 | pub old_val: bool, | ||
5 | pub val: bool, | ||
6 | } | ||
7 | |||
8 | impl ModifyRecord { | ||
9 | pub fn new(point: (i32, i32), old_val: bool, val: bool) -> Self { | ||
10 | ModifyRecord { | ||
11 | point, | ||
12 | old_val, | ||
13 | val, | ||
14 | } | ||
15 | } | ||
16 | } | ||
17 | |||
18 | pub enum OpKind { | ||
19 | Undo, | ||
20 | Redo, | ||
21 | } | ||
22 | |||
23 | pub type Operation = Vec<ModifyRecord>; | ||
24 | |||
25 | #[derive(Debug)] | ||
26 | pub struct UndoStack<T> { | ||
27 | operations: Vec<T>, | ||
28 | position: Option<u32>, | ||
29 | } | ||
30 | |||
31 | impl<T> UndoStack<T> | ||
32 | where | ||
33 | T: Clone, | ||
34 | { | ||
35 | pub fn new() -> Self { | ||
36 | Self { | ||
37 | operations: Vec::with_capacity(64), | ||
38 | position: None, | ||
39 | } | ||
40 | } | ||
41 | |||
42 | pub fn push(&mut self, op: T) { | ||
43 | if let Some(p) = self.position { | ||
44 | // remove all operations past the newly pushed operation | ||
45 | for _ in 1 + (p as usize)..self.operations.len() { | ||
46 | self.operations.pop(); | ||
47 | } | ||
48 | // advance position | ||
49 | self.position = Some(p + 1); | ||
50 | // add new operation | ||
51 | self.operations.push(op); | ||
52 | } else { | ||
53 | // empty ops list or undone till start of stack | ||
54 | // remove all operations past the newly pushed operation | ||
55 | self.operations.clear(); | ||
56 | // advance position | ||
57 | self.position = Some(0); | ||
58 | // add new operation | ||
59 | self.operations.push(op); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | pub fn undo(&mut self) -> Option<T> { | ||
64 | if let Some(p) = self.position { | ||
65 | self.position = p.checked_sub(1); | ||
66 | // we want to return a clone and not a reference because push deletes the item | ||
67 | return Some(self.operations[p as usize].clone()); | ||
68 | } | ||
69 | return None; | ||
70 | } | ||
71 | |||
72 | pub fn redo(&mut self) -> Option<T> { | ||
73 | if let Some(p) = self.position { | ||
74 | if p < self.operations.len() as u32 - 1 { | ||
75 | self.position = Some(p + 1); | ||
76 | return Some(self.operations[1 + p as usize].clone()); | ||
77 | } | ||
78 | } else if !self.operations.is_empty() { | ||
79 | self.position = Some(0); | ||
80 | return Some(self.operations[0].clone()); | ||
81 | } | ||
82 | return None; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | #[cfg(test)] | ||
87 | mod tests { | ||
88 | use super::*; | ||
89 | |||
90 | fn setup() -> UndoStack<u32> { | ||
91 | let mut stack = UndoStack::new(); | ||
92 | stack.push(10); | ||
93 | stack.push(5); | ||
94 | stack.push(2); | ||
95 | stack | ||
96 | } | ||
97 | |||
98 | #[test] | ||
99 | fn undo_works() { | ||
100 | let mut stack = setup(); | ||
101 | assert_eq!(stack.undo(), Some(2)); | ||
102 | assert_eq!(stack.undo(), Some(5)); | ||
103 | assert_eq!(stack.undo(), Some(10)); | ||
104 | } | ||
105 | #[test] | ||
106 | fn redo_works() { | ||
107 | let mut stack = setup(); | ||
108 | stack.undo(); | ||
109 | stack.undo(); | ||
110 | stack.undo(); | ||
111 | assert_eq!(stack.redo(), Some(10)); | ||
112 | assert_eq!(stack.redo(), Some(5)); | ||
113 | assert_eq!(stack.redo(), Some(2)); | ||
114 | } | ||
115 | |||
116 | #[test] | ||
117 | fn undo_push_redo() { | ||
118 | let mut stack = setup(); | ||
119 | stack.undo(); | ||
120 | stack.push(16); | ||
121 | assert_eq!(stack.redo(), None); | ||
122 | assert_eq!(stack.undo(), Some(16)); | ||
123 | } | ||
124 | |||
125 | #[test] | ||
126 | fn stack_identity() { | ||
127 | let mut stack = setup(); | ||
128 | stack.undo(); | ||
129 | stack.redo(); | ||
130 | stack.undo(); | ||
131 | assert_eq!(stack.operations, setup().operations); | ||
132 | } | ||
133 | } | ||