From 9f0dc65dabff2a1e443199ed68292398dcb390b1 Mon Sep 17 00:00:00 2001 From: Akshay Date: Fri, 12 Mar 2021 20:24:55 +0530 Subject: init --- src/undo.rs | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/undo.rs (limited to 'src/undo.rs') diff --git a/src/undo.rs b/src/undo.rs new file mode 100644 index 0000000..2249fe7 --- /dev/null +++ b/src/undo.rs @@ -0,0 +1,133 @@ +#[derive(Copy, Clone, Debug)] +pub struct ModifyRecord { + pub point: (i32, i32), + pub old_val: bool, + pub val: bool, +} + +impl ModifyRecord { + pub fn new(point: (i32, i32), old_val: bool, val: bool) -> Self { + ModifyRecord { + point, + old_val, + val, + } + } +} + +pub enum OpKind { + Undo, + Redo, +} + +pub type Operation = Vec; + +#[derive(Debug)] +pub struct UndoStack { + operations: Vec, + position: Option, +} + +impl UndoStack +where + T: Clone, +{ + pub fn new() -> Self { + Self { + operations: Vec::with_capacity(64), + position: None, + } + } + + pub fn push(&mut self, op: T) { + if let Some(p) = self.position { + // remove all operations past the newly pushed operation + for _ in 1 + (p as usize)..self.operations.len() { + self.operations.pop(); + } + // advance position + self.position = Some(p + 1); + // add new operation + self.operations.push(op); + } else { + // empty ops list or undone till start of stack + // remove all operations past the newly pushed operation + self.operations.clear(); + // advance position + self.position = Some(0); + // add new operation + self.operations.push(op); + } + } + + pub fn undo(&mut self) -> Option { + if let Some(p) = self.position { + self.position = p.checked_sub(1); + // we want to return a clone and not a reference because push deletes the item + return Some(self.operations[p as usize].clone()); + } + return None; + } + + pub fn redo(&mut self) -> Option { + if let Some(p) = self.position { + if p < self.operations.len() as u32 - 1 { + self.position = Some(p + 1); + return Some(self.operations[1 + p as usize].clone()); + } + } else if !self.operations.is_empty() { + self.position = Some(0); + return Some(self.operations[0].clone()); + } + return None; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn setup() -> UndoStack { + let mut stack = UndoStack::new(); + stack.push(10); + stack.push(5); + stack.push(2); + stack + } + + #[test] + fn undo_works() { + let mut stack = setup(); + assert_eq!(stack.undo(), Some(2)); + assert_eq!(stack.undo(), Some(5)); + assert_eq!(stack.undo(), Some(10)); + } + #[test] + fn redo_works() { + let mut stack = setup(); + stack.undo(); + stack.undo(); + stack.undo(); + assert_eq!(stack.redo(), Some(10)); + assert_eq!(stack.redo(), Some(5)); + assert_eq!(stack.redo(), Some(2)); + } + + #[test] + fn undo_push_redo() { + let mut stack = setup(); + stack.undo(); + stack.push(16); + assert_eq!(stack.redo(), None); + assert_eq!(stack.undo(), Some(16)); + } + + #[test] + fn stack_identity() { + let mut stack = setup(); + stack.undo(); + stack.redo(); + stack.undo(); + assert_eq!(stack.operations, setup().operations); + } +} -- cgit v1.2.3