1#[derive(Debug)]
8pub struct History<T> {
9 entries: Vec<T>,
10 forward_entries: Vec<T>,
11 max_entries: usize,
12}
13
14impl<T> History<T> {
15 pub fn new() -> Self {
16 Self {
17 entries: Vec::new(),
18 forward_entries: Vec::new(),
19 max_entries: 1000,
20 }
21 }
22
23 pub fn max_entries(mut self, max_entries: usize) -> Self {
27 self.max_entries = max_entries;
28 self.enforce_max_entries();
29 self
30 }
31
32 pub fn push(&mut self, entry: T) {
34 self.forward_entries.clear();
35 if self.max_entries == 0 {
36 return;
37 }
38 self.entries.push(entry);
39 self.enforce_max_entries();
40 }
41
42 pub fn current(&self) -> Option<&T> {
44 self.entries.last()
45 }
46
47 pub fn replace_current(&mut self, entry: T) {
49 match self.entries.last_mut() {
50 Some(current) => *current = entry,
51 None => self.push(entry),
52 }
53 }
54
55 pub fn remove_current(&mut self) -> Option<T> {
57 self.entries.pop()
58 }
59
60 pub fn can_back(&self) -> bool {
62 self.entries.len() > 1
63 }
64
65 pub fn can_forward(&self) -> bool {
67 !self.forward_entries.is_empty()
68 }
69
70 pub fn back(&mut self) -> Option<T>
72 where
73 T: Clone,
74 {
75 if self.entries.len() <= 1 {
76 return None;
77 }
78 self.forward_entries.push(self.entries.pop().unwrap());
79 self.current().cloned()
80 }
81
82 pub fn forward(&mut self) -> Option<T>
84 where
85 T: Clone,
86 {
87 if self.max_entries == 0 {
88 return None;
89 }
90 let entry = self.forward_entries.pop()?;
91 self.entries.push(entry);
92 self.enforce_max_entries();
93 self.current().cloned()
94 }
95
96 pub fn entries(&self) -> impl DoubleEndedIterator<Item = &T> + ExactSizeIterator {
98 self.entries.iter()
99 }
100
101 pub fn forward_entries(&self) -> impl DoubleEndedIterator<Item = &T> + ExactSizeIterator {
103 self.forward_entries.iter().rev()
104 }
105
106 pub fn retain(&mut self, mut keep: impl FnMut(&T) -> bool) {
108 self.entries.retain(&mut keep);
109 self.forward_entries.retain(&mut keep);
110 }
111
112 pub fn clear(&mut self) {
114 self.entries.clear();
115 self.forward_entries.clear();
116 }
117
118 fn enforce_max_entries(&mut self) {
119 let excess = self.entries.len().saturating_sub(self.max_entries);
120 if excess > 0 {
121 self.entries.drain(..excess);
122 }
123 }
124}
125
126impl<T> Default for History<T> {
127 fn default() -> Self {
128 Self::new()
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn navigation_moves_between_entries_without_backing_past_the_root() {
138 let mut history = History::new().max_entries(3);
139 history.push(1);
140 history.push(2);
141 history.push(3);
142
143 assert_eq!(history.current(), Some(&3));
144 assert_eq!(history.back(), Some(2));
145 assert_eq!(history.back(), Some(1));
146 assert_eq!(history.back(), None);
147 assert_eq!(history.forward(), Some(2));
148 assert_eq!(history.entries().copied().collect::<Vec<_>>(), [1, 2]);
149 assert_eq!(history.entries().rev().copied().collect::<Vec<_>>(), [2, 1]);
150 assert_eq!(history.forward_entries().copied().collect::<Vec<_>>(), [3]);
151 assert!(history.can_back());
152 assert!(history.can_forward());
153 }
154
155 #[test]
156 fn pushing_after_back_truncates_the_forward_branch() {
157 let mut history = History::new();
158 history.push(1);
159 history.push(2);
160 history.push(3);
161 assert_eq!(history.back(), Some(2));
162
163 history.push(4);
164
165 assert_eq!(history.entries().copied().collect::<Vec<_>>(), [1, 2, 4]);
166 assert!(!history.can_forward());
167 assert_eq!(history.forward(), None);
168 }
169
170 #[test]
171 fn repeated_entries_preserve_every_navigation_step() {
172 let mut history = History::new();
173 history.push(1);
174 history.push(2);
175 history.push(1);
176
177 assert_eq!(history.entries().copied().collect::<Vec<_>>(), [1, 2, 1]);
178 assert_eq!(history.back(), Some(2));
179 assert_eq!(history.back(), Some(1));
180 }
181
182 #[test]
183 fn max_entries_evicts_the_oldest_entry() {
184 let mut history = History::new().max_entries(2);
185 history.push(1);
186 history.push(2);
187 history.push(3);
188
189 assert_eq!(history.entries().copied().collect::<Vec<_>>(), [2, 3]);
190 assert_eq!(history.back(), Some(2));
191 assert_eq!(history.back(), None);
192 }
193
194 #[test]
195 fn lowering_max_entries_truncates_populated_entries_and_caps_forward_restores() {
196 let mut history = History::new();
197 history.push(1);
198 history.push(2);
199 history.push(3);
200 assert_eq!(history.back(), Some(2));
201
202 history = history.max_entries(1);
203
204 assert_eq!(history.entries().copied().collect::<Vec<_>>(), [2]);
205 assert_eq!(history.forward(), Some(3));
206 assert_eq!(history.entries().copied().collect::<Vec<_>>(), [3]);
207 assert_eq!(history.back(), None);
208 }
209
210 #[test]
211 fn zero_max_entries_retains_nothing() {
212 let mut history = History::new().max_entries(0);
213 history.push(1);
214
215 assert_eq!(history.current(), None);
216 assert_eq!(history.entries().len(), 0);
217 assert!(!history.can_back());
218 assert!(!history.can_forward());
219 }
220
221 #[test]
222 fn replace_current_updates_in_place_and_pushes_when_empty() {
223 let mut history = History::new();
224 history.replace_current(1);
225 history.push(2);
226 history.replace_current(3);
227
228 assert_eq!(history.entries().copied().collect::<Vec<_>>(), [1, 3]);
229 }
230
231 #[test]
232 fn remove_current_preserves_forward_entries() {
233 let mut history = History::new();
234 history.push(1);
235 history.push(2);
236 history.push(3);
237 assert_eq!(history.back(), Some(2));
238
239 assert_eq!(history.remove_current(), Some(2));
240
241 assert_eq!(history.current(), Some(&1));
242 assert_eq!(history.forward_entries().copied().collect::<Vec<_>>(), [3]);
243 assert_eq!(history.forward(), Some(3));
244 }
245
246 #[test]
247 fn retain_filters_back_and_forward_entries_without_reordering() {
248 let mut history = History::new();
249 for entry in 1..=8 {
250 history.push(entry);
251 }
252 history.back();
253 history.back();
254 history.back();
255 history.back();
256
257 history.retain(|entry| entry % 2 == 0);
258
259 assert_eq!(history.entries().copied().collect::<Vec<_>>(), [2, 4]);
260 assert_eq!(
261 history.forward_entries().copied().collect::<Vec<_>>(),
262 [6, 8]
263 );
264 assert_eq!(history.forward(), Some(6));
265 assert_eq!(history.forward(), Some(8));
266 }
267
268 #[test]
269 fn clear_removes_back_and_forward_entries() {
270 let mut history = History::new();
271 history.push(1);
272 history.push(2);
273 history.back();
274
275 history.clear();
276
277 assert_eq!(history.current(), None);
278 assert_eq!(history.entries().len(), 0);
279 assert_eq!(history.forward_entries().len(), 0);
280 assert!(!history.can_back());
281 assert!(!history.can_forward());
282 }
283}