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
110
111
112
113
114
/// A generic undo/redo stack.
///
/// Stores snapshots of state. Calling [`undo`](UndoStack::undo) moves backward
/// through history; [`redo`](UndoStack::redo) moves forward. Pushing a new
/// item after undoing discards the redo branch.
///
/// # Type parameters
///
/// `T` must implement `Clone` because the stack stores owned snapshots.
#[derive(Debug, Clone)]
pub struct UndoStack<T: Clone> {
history: Vec<T>,
index: usize,
}
impl<T: Clone> UndoStack<T> {
/// Create an empty undo stack.
pub fn new() -> Self {
Self {
history: Vec::new(),
index: 0,
}
}
/// Push a new state snapshot onto the stack.
///
/// Any redo history after the current position is discarded.
pub fn push(&mut self, item: T) {
self.history.truncate(self.index + 1);
self.history.push(item);
self.index = self.history.len().saturating_sub(1);
}
/// Move one step backward in history and return the state at the new
/// position.
///
/// If already at the earliest state, returns that state again.
pub fn undo(&mut self) -> Option<&T> {
if self.index == 0 {
self.history.first()
} else {
self.index -= 1;
self.history.get(self.index)
}
}
/// Move one step forward in history and return the state at the new
/// position.
///
/// Returns `None` if already at the most recent state.
pub fn redo(&mut self) -> Option<&T> {
if self.index + 1 < self.history.len() {
self.index += 1;
self.history.get(self.index)
} else {
None
}
}
/// Return the state at the current position without changing it.
pub fn current(&self) -> Option<&T> {
self.history.get(self.index)
}
}
impl<T: Clone> Default for UndoStack<T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn current_returns_none_when_empty() {
let stack: UndoStack<i32> = UndoStack::new();
assert!(stack.current().is_none());
}
#[test]
fn redo_at_end_returns_none() {
let mut stack = UndoStack::new();
stack.push(1);
assert!(stack.redo().is_none());
}
#[test]
fn undo_redo_sequence() {
let mut stack = UndoStack::new();
stack.push(1);
stack.push(2);
stack.push(3);
assert_eq!(stack.current(), Some(&3));
assert_eq!(stack.undo(), Some(&2));
assert_eq!(stack.undo(), Some(&1));
assert_eq!(stack.undo(), Some(&1));
assert_eq!(stack.redo(), Some(&2));
assert_eq!(stack.redo(), Some(&3));
assert_eq!(stack.redo(), None);
}
#[test]
fn push_after_undo_overwrites() {
let mut stack = UndoStack::new();
stack.push(1);
stack.push(2);
stack.undo();
stack.push(3);
assert_eq!(stack.current(), Some(&3));
assert_eq!(stack.redo(), None);
}
}