1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#![doc = include_str!("../README.md")]

pub mod data_structures;
pub use aper_derive::StateMachine;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;

pub mod sync;

/// An unconstructable type, meant to serve as the `Conflict` type of a [`StateMachine`]
/// for which a conflict can never arise.
///
/// This is similar to [`std::convert::Infallible`], but with some derives to allow
/// it to be used as a [`StateMachine::Conflict`].
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub enum NeverConflict {}

/// This trait provides the methods that Aper needs to be able to interact with
/// an object as a state machine.
///
/// None of the methods in this trait provide access to the internal data of the
/// state machine. It's up to you to implement accessor methods (or use public
/// fields) in order to expose the data necessary to render your views.
pub trait StateMachine: Clone + DeserializeOwned + Serialize + Debug + 'static {
    /// The [`StateMachine::Transition`] type associates another type with this state machine
    /// as its transitions.
    type Transition: Debug + Serialize + DeserializeOwned + Clone + PartialEq;
    type Conflict: Debug + Serialize + DeserializeOwned + Clone + PartialEq;

    /// Update the state machine according to the given [`Transition`]. This method *must* be
    /// deterministic: calling it on a clone of the state with a clone of the [`Transition`]
    /// must result in the same state, even at a different time and on a different machine. This
    /// is the requirement that allows Aper to keep the state in sync across multiple machines.
    fn apply(&self, transition: &Self::Transition) -> Result<Self, Self::Conflict>;
}