#track #undo #history

cset

Fine-grained and reversible struct transactions

2 releases

0.1.1 Dec 24, 2022
0.1.0 Dec 22, 2022

#1304 in Data structures

MIT/Apache

8KB
90 lines

drates.io docs.rs

cset

Fine-grained and reversible struct transactions.

This crate offers a Track macro that structs can derive to generate the plumbing needed to precisely track changes to fields. Calling Trackable::edit() returns a Draft that stores edits separately from the underyling struct, such that no values are written.

When .commit() is called on the draft, edits are applied to the base struct. Each replaced value is returned to the caller as a Change in a ChangeSet. This changeset can then be re-applied to a struct of the same type, which replaces fields with values from the ChangeSet. This operation produces a new ChangeSet, allowing for the implementation of an undo-redo paradigm.

Project status

This project is early in development and API changes should be expected.


lib.rs:

Fine-grained and reversible struct transactions.

This crate offers a Track macro that structs can derive to generate the plumbing needed to precisely track changes to fields. Calling edit() returns a draft that stores edits separately from the underyling struct, such that no values are written.

When apply() is called on the draft, edits are applied to the base struct. Each replaced value is returned to the caller as a [Change] in a [ChangeSet]. This changeset can then be re-applied to a struct of the same type, which replaces fields with values from the [ChangeSet]. This operation produces a new [ChangeSet], allowing for the implementation of an undo-redo paradigm.

Example

use cset::Track;

#[derive(Track)]
struct Foo {
    x: usize,
    #[track(flatten)]
    bar: Bar,
}

#[derive(Track)]
struct Bar {
    y: usize,
}

let mut foo = Foo::new(10, Bar::new(42));

// Enter the non-destructive editing mode
let mut foo_draft = foo.edit();
foo_draft.set_x(42);
foo_draft.edit_bar().set_y(1024);

// Drop the draft to rollback, or apply the changes with `.apply()`
let undo_changeset = foo_draft.apply();
assert_eq!(foo, Foo::new(42, Bar::new(1024)));

let redo_changeset = foo.apply(undo_changeset);
assert_eq!(foo, Foo::new(10, Bar::new(42)));

foo.apply(redo_changeset);
assert_eq!(foo, Foo::new(42, Bar::new(1024)));

Dependencies

~1.5MB
~34K SLoC