Crate gur

source ·
Expand description

Wrapper types to provide undo-redo functionality.

Sample code

use gur::prelude::*;
use gur::cur::{Cur, CurBuilder};

// Appication state
#[derive(Clone)]
struct MyState {
    data: String
}

fn main() {
    // Initialize
    let mut state: Cur<MyState> = CurBuilder::new().build(MyState{ data: "My".to_string() });
    assert_eq!("My", state.data);

    // Change state
    state.edit(|mut state: MyState| { state.data += "State"; state });
    assert_eq!("MyState", state.data);

    // Undo
    state.undo();
    assert_eq!("My", state.data);

    // Redo
    state.redo();
    assert_eq!("MyState", state.data);
}

Where Cur<T> is a type providing undo-redo functionality. The MyState is a type of user’s application state. MyState implements the Clone trait required by Cur<T>. Then the variable state as type Cur<MyState> is created to get the ability to undo-redo.

The edit takes a closure to change the variable. The closure is a function that consumes a current state as internal type MyState and returns a new state. undo can restore the previous state. The redo is reverse operation of the undo.

The Cur<T> implements Deref. So the variable can be used as like smart pointers.

Ur family

Ur is a most basic type in this crate. Some variants are provided in this crate. The variants and their features are listed below.

TypeRequirementsThread safetyDescription
Ur<T>T: SnapshotNoA basic wrapper for types implementing Snapshot.
Cur<T>T: CloneNoAnother simple wrapper for types implementing Clone.
Aur<T>T: SnapshotYesUr<T> + Send + Sync
Acur<T>T: CloneYesCur<T> + Send + Sync

Requirements

For example, Ur<T> requires T implementing Snapshot. On the other hand, Cur<T> requires Clone instead of Snapshot for simplicity.

Thread safety

Some types of them can be Send and Sync and some not. See interface for more details about thread safety.

Generative approach

This section describes a basic concept of this crate. The concept is that “Undoing is regenerating (recomputing) the old state.”

For explanation, a sample history of changes is shown as follows,

t: state
c: command
s: snapshot

old <----------------------> new
     c1        c2        c3
t0 -----> t1 -----> t2 -----> t3
|  +-------------->           |
|  |                          |
s0 +--------------------------+
           undo t3 -> t2

Where tx is an application state at time point x, cx is a command to change a state tx-1 to next tx, and sx is a snapshot of a state tx.

The application state have changed in order of t0, t1, t2, and t3. Now, the current state is t3.

Let us consider undoing the current state t3 to its previous state t2. First, the system restores an old state from its snapshot at any point in the history. In this case, We would have to restore the state t0 from s0 because there is only one snapshot s0. Then the system reruns the commands (c1 and c2) in order. Finally, the target state t2 will be obtained.

Data structure

A history is managed as chain of commands and snapshots to perform the process described above. No intermediate states are stored.

c: command
s: snapshot

old <----------------------------------------------> new
/--\  +--+  +--+       +--+  /----\  +----+  +----+
|s0|--|c1|--|c2|--...--|cn|--|sn+1|--|cn+2|--|cn+3|--...
\--/  +--+  +--+       +--+  \----/  +----+  +----+

The frequency of snapshots can be customized by “trigger functions.” See crate::triggers for more details.

Pros and Cons

Pros

The advantages of this approach are usability and robustness. There are no backward opeartions in the undoing process. So users almost never have to write additional codes for the process. If there are tests for the application state object, the correctness of undo-redo process is also guaranteed.

Cons

Users should pay attention to side effects of commands.

See also edit with side effects” for more details.

Modules

Thread-safe Cur<T>
Thread-safe Ur<T>
Simplified Ur<T>
Interfaces of Ur and related types.
Metrics of commands
Re-export common members
Snapshot
Triggers documentation
A basic undo-redo functionality.