Skip to main content

patterns/
context.rs

1use patterns_macros::because;
2
3pub trait Context {
4    type State;
5    type Command;
6    type Event;
7    type Fault;
8
9    fn initial() -> Self::State;
10    fn decide(state: &Self::State, command: &Self::Command) -> Result<Self::Event, Self::Fault>;
11    fn apply(state: Self::State, event: &Self::Event) -> Self::State;
12}
13
14pub fn replay_from<C: Context>(state: C::State, events: &[C::Event]) -> C::State {
15    let mut next = state;
16    for event in events {
17        next = C::apply(next, event);
18    }
19    next
20}
21
22pub fn replay<C: Context>(events: &[C::Event]) -> C::State {
23    replay_from::<C>(C::initial(), events)
24}
25
26pub fn handle<C: Context>(state: C::State, command: &C::Command) -> Result<C::State, C::Fault> {
27    match C::decide(&state, command) {
28        Ok(event) => Ok(C::apply(state, &event)),
29        Err(fault) => Err(fault),
30    }
31}
32
33pub fn handle_all<C: Context>(
34    state: C::State,
35    commands: &[C::Command],
36) -> Result<C::State, (usize, C::Fault)> {
37    let mut next = state;
38    for (at, command) in commands.iter().enumerate() {
39        match handle::<C>(next, command) {
40            Ok(moved) => next = moved,
41            Err(fault) => return Err((at, fault)),
42        }
43    }
44    Ok(next)
45}
46because!(replay_from, "applying a run of events to a state that already exists, which is how replay works forward from initial and how a caller holding a snapshot catches up without folding the whole log again");
47because!(replay, "folding a whole event log into the current state, so state is always derived and never stored");
48because!(handle, "deciding on a command and applying the resulting event, so a caller cannot apply an event the rules did not produce");
49because!(handle_all, "handle over a sequence, stopping at the first fault and naming the position of the command that raised it, because a partially applied batch is a state no replay can reach and a caller can only report or retry a batch when it knows where it broke");