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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//! An undo/redo library.
//!
//! # About
//! It uses the [Command Pattern] where the user implements the `UndoCmd` trait for each command.
//!
//! The `UndoStack` has two states, clean and dirty. The stack is clean when no more commands can
//! be redone, otherwise it is dirty. The stack will notice when it's state changes to either dirty
//! or clean, and call the user defined methods set in [`on_clean`] and [`on_dirty`]. This is useful if
//! you want to trigger some event when the state changes, eg. enabling and disabling buttons in an ui.
//!
//! It also supports [automatic merging] of commands with the same id.
//!
//! # Redo vs Undo
//! |                 | Redo         | Undo            |
//! |-----------------|--------------|-----------------|
//! | Dispatch        | Static       | Dynamic         |
//! | State Handling  | Yes          | Yes             |
//! | Command Merging | Yes (manual) | Yes (automatic) |
//!
//! `undo` uses [dynamic dispatch] instead of [static dispatch] to store the commands, which means
//! it has some additional overhead compared to [`redo`]. However, this has the benefit that you
//! can store multiple types of commands in a `UndoStack` at a time. Both supports state handling
//! and command merging but `undo` will automatically merge commands with the same id, while
//! in `redo` you need to implement the merge method yourself.
//!
//! I recommend using `undo` by default and to use `redo` when performance is important.
//! They have similar API, so it should be easy to switch between them if necessary.
//!
//! # Examples
//! ```
//! use undo::{self, UndoCmd, UndoStack};
//!
//! #[derive(Clone, Copy)]
//! struct PopCmd {
//!     vec: *mut Vec<i32>,
//!     e: Option<i32>,
//! }
//!
//! impl UndoCmd for PopCmd {
//!     type Err = ();
//!
//!     fn redo(&mut self) -> undo::Result<()> {
//!         self.e = unsafe {
//!             let ref mut vec = *self.vec;
//!             vec.pop()
//!         };
//!         Ok(())
//!     }
//!
//!     fn undo(&mut self) -> undo::Result<()> {
//!         unsafe {
//!             let ref mut vec = *self.vec;
//!             let e = self.e.ok_or(())?;
//!             vec.push(e);
//!         }
//!         Ok(())
//!     }
//! }
//!
//! fn foo() -> undo::Result<()> {
//!     let mut vec = vec![1, 2, 3];
//!     let mut stack = UndoStack::new();
//!     let cmd = PopCmd { vec: &mut vec, e: None };
//!
//!     stack.push(cmd)?;
//!     stack.push(cmd)?;
//!     stack.push(cmd)?;
//!
//!     assert!(vec.is_empty());
//!
//!     stack.undo()?;
//!     stack.undo()?;
//!     stack.undo()?;
//!
//!     assert_eq!(vec.len(), 3);
//!     Ok(())
//! }
//! # foo().unwrap();
//! ```
//!
//! *An unsafe implementation of `redo` and `undo` is used in examples since it is less verbose and
//! makes the examples easier to follow.*
//!
//! [Command Pattern]: https://en.wikipedia.org/wiki/Command_pattern
//! [`on_clean`]: struct.UndoStack.html#method.on_clean
//! [`on_dirty`]: struct.UndoStack.html#method.on_dirty
//! [automatic merging]: trait.UndoCmd.html#method.id
//! [static dispatch]: https://doc.rust-lang.org/stable/book/trait-objects.html#static-dispatch
//! [dynamic dispatch]: https://doc.rust-lang.org/stable/book/trait-objects.html#dynamic-dispatch
//! [`redo`]: https://crates.io/crates/redo

extern crate fnv;

mod group;
mod stack;

pub use group::UndoGroup;
pub use stack::UndoStack;

use std::fmt;
use std::result;

type Key = u32;

/// An unique id for an `UndoStack`.
#[derive(Debug)]
pub struct Id(Key);

/// A specialized `Result` that does not carry any data on success.
pub type Result<E> = result::Result<(), E>;

/// Trait that defines the functionality of a command.
///
/// Every command needs to implement this trait to be able to be used with the `UndoStack`.
pub trait UndoCmd {
    /// The error type.
    ///
    /// This needs to be the same for all `UndoCmd`s that is going to be used in the same stack or
    /// group.
    type Err;

    /// Executes the desired command and returns `Ok` if everything went fine, and `Err` if
    /// something went wrong.
    fn redo(&mut self) -> Result<Self::Err>;

    /// Restores the state as it was before [`redo`] was called and returns `Ok` if everything
    /// went fine, and `Err` if something went wrong.
    ///
    /// [`redo`]: trait.UndoCmd.html#tymethod.redo
    fn undo(&mut self) -> Result<Self::Err>;

    /// Used for merging of `UndoCmd`s.
    ///
    /// Two commands are merged together when a command is pushed on the `UndoStack`, and it has
    /// the same id as the top command already on the stack. When commands are merged together,
    /// undoing and redoing them are done in one step. An example where this is useful is a text
    /// editor where you might want to undo a whole word instead of each character.
    ///
    /// Default implementation returns `None`, which means the command will never be merged.
    ///
    /// # Examples
    /// ```
    /// use undo::{self, UndoCmd, UndoStack};
    ///
    /// #[derive(Clone, Copy)]
    /// struct PopCmd {
    ///     vec: *mut Vec<i32>,
    ///     e: Option<i32>,
    /// }
    ///
    /// impl UndoCmd for PopCmd {
    ///     type Err = ();
    ///
    ///     fn redo(&mut self) -> undo::Result<()> {
    ///         self.e = unsafe {
    ///             let ref mut vec = *self.vec;
    ///             vec.pop()
    ///         };
    ///         Ok(())
    ///     }
    ///
    ///     fn undo(&mut self) -> undo::Result<()> {
    ///         unsafe {
    ///             let ref mut vec = *self.vec;
    ///             let e = self.e.ok_or(())?;
    ///             vec.push(e);
    ///         }
    ///         Ok(())
    ///     }
    ///
    ///     fn id(&self) -> Option<u64> {
    ///         Some(1)
    ///     }
    /// }
    ///
    /// fn foo() -> undo::Result<()> {
    ///     let mut vec = vec![1, 2, 3];
    ///     let mut stack = UndoStack::new();
    ///     let cmd = PopCmd { vec: &mut vec, e: None };
    ///
    ///     stack.push(cmd)?;
    ///     stack.push(cmd)?;
    ///     stack.push(cmd)?;
    ///
    ///     assert!(vec.is_empty());
    ///     stack.undo()?;
    ///     assert_eq!(vec.len(), 3);
    ///     stack.redo()?;
    ///     assert!(vec.is_empty());
    ///     Ok(())
    /// }
    /// # foo().unwrap();
    /// ```
    #[inline]
    fn id(&self) -> Option<u64> {
        None
    }
}

impl<'a, E> fmt::Debug for UndoCmd<Err = E> + 'a {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.id() {
            Some(id) => write!(f, "{}", id),
            None => write!(f, "_"),
        }
    }
}