1use crate::value::Value;
2
3pub const DEFAULT_HISTORY_DEPTH: usize = 10;
4pub const DEFAULT_HISTORY_CAPACITY: usize = DEFAULT_HISTORY_DEPTH + 1;
5
6#[derive(Debug, Clone)]
7pub struct History {
8 snapshots: Vec<Vec<Value>>,
9 position: usize,
10 capacity: usize,
11}
12
13impl Default for History {
14 fn default() -> Self {
15 Self::new()
16 }
17}
18
19impl History {
20 pub fn new() -> Self {
21 Self::with_capacity(DEFAULT_HISTORY_CAPACITY)
22 }
23
24 pub fn with_capacity(capacity: usize) -> Self {
25 Self {
26 snapshots: Vec::new(),
27 position: 0,
28 capacity,
29 }
30 }
31
32 pub fn capacity(&self) -> usize {
33 self.capacity
34 }
35
36 pub fn is_enabled(&self) -> bool {
37 self.capacity >= 2
38 }
39
40 pub fn position(&self) -> usize {
41 self.position
42 }
43
44 pub fn len(&self) -> usize {
45 self.snapshots.len()
46 }
47
48 pub fn is_empty(&self) -> bool {
49 self.snapshots.is_empty()
50 }
51
52 pub fn can_undo(&self) -> bool {
53 self.position > 0
54 }
55
56 pub fn can_redo(&self) -> bool {
57 self.position + 1 < self.snapshots.len()
58 }
59
60 pub fn undo_count(&self) -> usize {
61 self.position
62 }
63
64 pub fn redo_count(&self) -> usize {
65 if self.snapshots.is_empty() {
66 0
67 } else {
68 self.snapshots.len() - 1 - self.position
69 }
70 }
71
72 pub fn undo(&mut self) -> Option<Vec<Value>> {
73 if !self.is_enabled() || self.position == 0 {
74 return None;
75 }
76 self.position -= 1;
77 Some(self.snapshots[self.position].clone())
78 }
79
80 pub fn redo(&mut self) -> Option<Vec<Value>> {
81 if !self.is_enabled() || self.position + 1 >= self.snapshots.len() {
82 return None;
83 }
84 self.position += 1;
85 Some(self.snapshots[self.position].clone())
86 }
87
88 pub fn record(&mut self, snapshot: Vec<Value>) {
89 if !self.is_enabled() {
90 return;
91 }
92
93 self.snapshots.truncate(self.position + 1);
95
96 if !self.snapshots.is_empty() && snapshot == *self.snapshots.last().unwrap() {
98 return;
99 }
100
101 if self.snapshots.len() == self.capacity && self.capacity > 0 {
103 self.snapshots.remove(0);
104 if self.position > 0 {
105 self.position -= 1;
106 }
107 }
108
109 self.snapshots.push(snapshot);
110 self.position = self.snapshots.len() - 1;
111 }
112
113 pub fn seed(&mut self, snapshot: Vec<Value>) {
114 if !self.is_enabled() {
115 return;
116 }
117 self.snapshots = vec![snapshot];
118 self.position = 0;
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use crate::value::Value;
126
127 #[test]
128 fn undo_disabled_at_zero_capacity() {
129 let mut h = History::with_capacity(0);
130 assert!(!h.is_enabled());
131 h.record(vec![Value::Int(1.into())]);
132 assert_eq!(h.len(), 0);
133 assert!(h.undo().is_none());
134 assert!(h.redo().is_none());
135 }
136
137 #[test]
138 fn capacity_one_disables() {
139 let h = History::with_capacity(1);
140 assert!(!h.is_enabled());
141 }
142
143 #[test]
144 fn capacity_three_transition() {
145 let mut h = History::with_capacity(3);
146 h.seed(vec![Value::Int(0.into())]); h.record(vec![Value::Int(1.into())]); h.record(vec![Value::Int(2.into())]); assert_eq!(h.len(), 3);
150 assert_eq!(h.position(), 2);
151 h.record(vec![Value::Int(3.into())]); assert_eq!(h.len(), 3);
153 assert_eq!(h.position(), 2);
154 let v = h.undo().unwrap();
156 assert_eq!(v, vec![Value::Int(2.into())]);
157 assert_eq!(h.position(), 1);
158 let v = h.redo().unwrap();
160 assert_eq!(v, vec![Value::Int(3.into())]);
161 assert_eq!(h.position(), 2);
162 }
163
164 #[test]
165 fn mutation_after_undo_drops_redo() {
166 let mut h = History::with_capacity(5);
167 h.seed(vec![Value::Int(0.into())]);
168 h.record(vec![Value::Int(1.into())]);
169 h.record(vec![Value::Int(2.into())]);
170 h.undo(); h.record(vec![Value::Int(99.into())]); assert_eq!(h.redo_count(), 0);
173 }
174
175 #[test]
176 fn no_op_result_truncates_redo_no_append() {
177 let mut h = History::with_capacity(5);
178 h.seed(vec![Value::Int(1.into())]);
179 h.record(vec![Value::Int(2.into())]);
180 h.undo(); h.record(vec![Value::Int(1.into())]);
184 assert_eq!(h.len(), 1);
187 }
188
189 #[test]
190 fn redo_count_empty_buffer() {
191 let h = History::with_capacity(5);
192 assert_eq!(h.redo_count(), 0);
193 }
194
195 #[test]
196 fn seed_empty_stack() {
197 let mut h = History::with_capacity(5);
198 h.seed(vec![]);
199 assert_eq!(h.len(), 1);
200 assert_eq!(h.position(), 0);
201 let v = h.undo();
202 assert!(v.is_none()); }
204
205 #[test]
206 fn new_delegates() {
207 let h = History::new();
208 assert_eq!(h.capacity(), DEFAULT_HISTORY_CAPACITY);
209 }
210}