redo 0.6.0

An undo/redo library with static dispatch, state handling and manual command merging.
Documentation

Redo

An undo/redo library with static dispatch, state handling and manual command merging.

Build Status Crates.io Docs

About

It uses the Command Pattern where the user implements the RedoCmd trait for a command.

The RedoStack has two states, clean and dirty. The stack is clean when no more commands can be redone, otherwise it is dirty. When it's state changes to either dirty or clean, it calls the user defined method set in on_state_change. This is useful if you want to trigger some event when the state changes, eg. enabling and disabling undo and redo buttons.

It also supports merging of commands by implementing the merge method for a command.

Redo vs Undo

Redo Undo
Dispatch Static Dynamic
State Handling Yes Yes
Command Merging Manual Auto

Both supports command merging but undo will automatically merge commands with the same id while in redo you need to implement the merge method yourself.

Examples

use redo::{self, RedoCmd, RedoStack};

#[derive(Clone, Copy)]
struct PopCmd {
    vec: *mut Vec<i32>,
    e: Option<i32>,
}

impl RedoCmd for PopCmd {
    type Err = &'static str;

    fn redo(&mut self) -> redo::Result<&'static str> {
        self.e = unsafe {
            let ref mut vec = *self.vec;
            vec.pop()
        };
        Ok(())
    }

    fn undo(&mut self) -> redo::Result<&'static str> {
        unsafe {
            let ref mut vec = *self.vec;
            let e = self.e.ok_or("`e` is invalid")?;
            vec.push(e);
        }
        Ok(())
    }
}

fn foo() -> redo::Result<&'static str> {
    let mut vec = vec![1, 2, 3];
    let mut stack = RedoStack::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(())
}