Crates.io | aper |
lib.rs | aper |
version | 0.5.0 |
source | src |
created_at | 2021-02-17 00:42:03.809286 |
updated_at | 2024-09-01 18:48:52.850678 |
description | Synchronized state machines over WebSockets |
homepage | https://aper.dev |
repository | |
max_upload_size | |
id | 356230 |
size | 52,800 |
Aper is a Rust library for data synchronization using state machines. Aper provides mechanisms to represent common data structures in terms of state machines, as well as a transport-agnostic protocol for keeping multiple instances of a state machine synchronized across a network.
Use-cases include real-time multiplayer applications that operate on shared state, client-server applications that want to share state updates incrementally and bidirectionally, and multiplayer turn-based games.
For the purposes of Aper, a state machine is simply a struct
or enum
that
implements StateMachine
and has the following properties:
StateMachine::Transition
type, through which every
possible change to the state can be described. It is usually useful,
though not required, that this be an enum
type.StateMachine::Conflict
type, which describes a conflict which
may occur when a transition is applied that is not valid at the time it is
applied. For simple types where a conflict is impossible, you can use
NeverConflict
for this.StateMachine
and a
Transition
, the result of applying the cloned transition to the cloned
state must be identical to applying the original transition to the original
state.Here's an example StateMachine
implementing a counter:
use aper::{Aper, AperSync};
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Clone, Debug, Default, AperSync)]
struct Counter { value: i64 }
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
enum CounterTransition {
Reset,
Increment(i64),
Decrement(i64),
}
impl Aper for Counter {
type Transition = CounterTransition;
type Conflict = NeverConflict;
fn apply(&self, event: &CounterTransition) -> Result<Counter, NeverConflict> {
match event {
CounterTransition::Reset => Ok(Counter {value: 0}),
CounterTransition::Increment(amount) => Ok(Counter {value: self.value + amount}),
CounterTransition::Decrement(amount) => Ok(Counter {value: self.value - amount}),
}
}
}
Conflict-free replicated data types are a really neat way of representing data that's shared between peers. In order to avoid the need for a central “source of truth”, CRDTs require that update operations (i.e. state transitions) be commutative. This allows them to represent a bunch of common data structures, but doesn't allow you to represent arbitrarily complex update logic. By relying on a central authority, a state-machine approach allows you to implement data structures with arbitrary update logic, such as atomic moves of a value between two data structures, or the rules of a board game.
Aper is rapidly evolving. Consider this a technology preview. See the list of issues outstanding for version 1.0