const DEFAULT_CAPACITY: usize = 100;
pub struct UndoStack<T> {
undo: Vec<T>,
redo: Vec<T>,
capacity: usize,
}
impl<T: Clone> UndoStack<T> {
pub fn with_capacity(capacity: usize) -> Self {
Self {
undo: Vec::with_capacity(capacity),
redo: Vec::with_capacity(capacity),
capacity: capacity.max(1),
}
}
pub fn new() -> Self {
Self::with_capacity(DEFAULT_CAPACITY)
}
pub fn push(&mut self, state: T) {
self.redo.clear();
if self.undo.len() >= self.capacity {
self.undo.remove(0);
}
self.undo.push(state);
}
pub fn undo(&mut self) -> Option<&T> {
let state = self.undo.pop()?;
self.redo.push(state);
self.undo.last()
}
pub fn redo(&mut self) -> Option<&T> {
let state = self.redo.pop()?;
self.undo.push(state);
self.undo.last()
}
pub fn can_undo(&self) -> bool {
!self.undo.is_empty()
}
pub fn can_redo(&self) -> bool {
!self.redo.is_empty()
}
pub fn current(&self) -> Option<&T> {
self.undo.last()
}
pub fn undo_len(&self) -> usize {
self.undo.len()
}
pub fn redo_len(&self) -> usize {
self.redo.len()
}
pub fn clear(&mut self) {
self.undo.clear();
self.redo.clear();
}
}
impl<T: Clone> Default for UndoStack<T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_push_and_current() {
let mut stack: UndoStack<i32> = UndoStack::new();
assert!(stack.current().is_none());
stack.push(1);
stack.push(2);
stack.push(3);
assert_eq!(stack.current(), Some(&3));
assert_eq!(stack.undo_len(), 3);
}
#[test]
fn test_undo_redo_basic() {
let mut stack: UndoStack<&str> = UndoStack::new();
stack.push("a");
stack.push("b");
stack.push("c");
assert_eq!(stack.undo(), Some(&"b"));
assert!(stack.can_redo());
assert_eq!(stack.redo(), Some(&"c"));
assert!(!stack.can_redo());
}
#[test]
fn test_can_undo_can_redo() {
let mut stack: UndoStack<i32> = UndoStack::new();
assert!(!stack.can_undo());
assert!(!stack.can_redo());
stack.push(1);
assert!(stack.can_undo());
stack.undo();
assert!(!stack.can_undo());
assert!(stack.can_redo());
}
#[test]
fn test_push_clears_redo() {
let mut stack: UndoStack<i32> = UndoStack::new();
stack.push(1);
stack.push(2);
stack.push(3);
stack.undo();
stack.undo();
assert_eq!(stack.redo_len(), 2);
stack.push(99);
assert!(!stack.can_redo());
assert_eq!(stack.redo_len(), 0);
}
#[test]
fn test_capacity_limit() {
let mut stack: UndoStack<i32> = UndoStack::with_capacity(3);
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
assert_eq!(stack.undo_len(), 3);
assert_eq!(stack.current(), Some(&4));
assert_eq!(stack.undo(), Some(&3));
assert_eq!(stack.undo(), Some(&2));
assert_eq!(stack.undo(), None);
}
#[test]
fn test_undo_on_empty_returns_none() {
let mut stack: UndoStack<i32> = UndoStack::new();
assert_eq!(stack.undo(), None);
}
#[test]
fn test_redo_on_empty_returns_none() {
let mut stack: UndoStack<i32> = UndoStack::new();
assert_eq!(stack.redo(), None);
}
#[test]
fn test_clear() {
let mut stack: UndoStack<i32> = UndoStack::new();
stack.push(1);
stack.push(2);
stack.undo();
stack.clear();
assert!(!stack.can_undo());
assert!(!stack.can_redo());
assert!(stack.current().is_none());
}
#[test]
fn test_multiple_undo_redo_cycles() {
let mut stack: UndoStack<i32> = UndoStack::new();
for i in 0..5 {
stack.push(i);
}
assert_eq!(stack.undo(), Some(&3)); assert_eq!(stack.undo(), Some(&2)); assert_eq!(stack.undo(), Some(&1)); assert_eq!(stack.undo(), Some(&0)); assert_eq!(stack.undo(), None);
assert_eq!(stack.redo(), Some(&0));
assert_eq!(stack.redo(), Some(&1));
assert_eq!(stack.redo(), Some(&2));
assert_eq!(stack.redo(), Some(&3));
assert_eq!(stack.redo(), Some(&4));
assert_eq!(stack.redo(), None);
}
}