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