Aper is very early stage. Feel free to poke around but please be aware that examples may not be in sync with the repo or crates versions of Aper.

Aper is an MIT-licensed Rust library.

Getting Started Guide GitHub icon GitHub docs crates.io

Every data mutation is a first-class value.

Serialize them to synchronize state across a network, or persist them to create an audit log.

use aper::{StateMachine, data_structures::{List, Atom}};
// `List` represents an ordered list.
// `Atom` wraps a value to make it immutable
// except by replacement.

fn main() {
    let mut my_list: List<Atom<String>> = List::new();
    
    let (_id, transition) = my_list.append(Atom::new(
        "Hello Aper".to_string()));

    // `transition` represents the action of adding
    // "Hello Aper" to the list, but doesn’t actually
    // modify the data.

    my_list.apply(transition);

    // Now the transition is applied.
}

Mutations can be applied out-of-order.

Mutations encode intent, so concurrent mutations are cleanly applied where possible.

use aper::{StateMachine, data_structures::{List, Atom}};

fn main() {
    let mut my_list: List<Atom<u32>> = List::new();
    
    let (id1, transition1) = my_list.append(Atom::new(1));
    let (id2, transition2) = my_list.append(Atom::new(2));

    my_list.apply(transition2); // my_list = [2]
    my_list.apply(transition1); // my_list = [2, 1]

    let (_id3, transition3) = my_list
        .insert_between(&id2, &id1, Atom::new(3));

    let (_id4, transition4) = my_list
        .insert_between(&id2, &id1, Atom::new(4));

    my_list.apply(transition4); // my_list = [2, 4, 1]
    my_list.apply(transition3); // my_list = [2, 4, 3, 1]
}

Implement arbitrary update logic.

Define your own units of state that integrate seamlessly with Aper's built-in data structures.

use aper::{StateMachine, Transition};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug, Clone)]
struct Counter {value: i64}

#[derive(Transition, Serialize, Deserialize,
    Debug, Clone, PartialEq)]
enum CounterTransition {
   Add(i64),
   Subtract(i64),
   Reset,
}

impl StateMachine for Counter {
    type Transition = CounterTransition;

    fn apply(&mut self, event: CounterTransition) {
        match event {
            CounterTransition::Add(i) => {
                self.value += i;
            }
            CounterTransition::Subtract(i) => {
                self.value -= i;
            }
            CounterTransition::Reset => {
                self.value = 0;
            }
        }
    }
}