1use serde::{de::DeserializeOwned, Deserialize, Serialize};
2use std::collections::HashSet;
3use std::hash::Hash;
4
5pub trait Reducer<T>: Send + Sync {
7 fn reduce(&self, current: T, update: T) -> T;
8}
9
10pub struct Overwrite;
12impl<T> Reducer<T> for Overwrite {
13 fn reduce(&self, _current: T, update: T) -> T {
14 update
15 }
16}
17
18pub struct Append;
20impl<T> Reducer<Vec<T>> for Append {
21 fn reduce(&self, mut current: Vec<T>, mut update: Vec<T>) -> Vec<T> {
22 current.append(&mut update);
23 current
24 }
25}
26
27pub struct Union;
29impl<T: Eq + Hash> Reducer<HashSet<T>> for Union {
30 fn reduce(&self, mut current: HashSet<T>, update: HashSet<T>) -> HashSet<T> {
31 current.extend(update);
32 current
33 }
34}
35
36pub trait StateSchema:
37 Serialize + DeserializeOwned + Clone + Default + Send + Sync + std::fmt::Debug + 'static
38{
39 type Update: Serialize
40 + DeserializeOwned
41 + Clone
42 + Default
43 + Send
44 + Sync
45 + std::fmt::Debug
46 + 'static;
47
48 fn apply(current: &Self, update: Self::Update) -> Self;
49
50 fn trace_repr(&self) -> String {
53 serde_json::to_string_pretty(self).unwrap_or_else(|_| "<unserializable>".to_string())
54 }
55}
56
57pub trait StateReducer: StateSchema {
58 fn reduce(current: &Self, update: Self::Update) -> Self {
59 <Self as StateSchema>::apply(current, update)
60 }
61}
62
63impl<T: StateSchema> StateReducer for T {}
64
65#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
66#[serde(bound = "S: StateSchema")]
67pub struct GraphState<S: StateSchema> {
68 pub data: S,
69}
70
71impl<S: StateSchema> GraphState<S> {
72 pub fn new(data: S) -> Self {
73 Self { data }
74 }
75
76 pub fn apply_update(self, update: StateUpdate<S>) -> Self {
77 Self {
78 data: S::apply(&self.data, update.data),
79 }
80 }
81
82 pub fn apply(self, update: StateUpdate<S>) -> Self {
83 self.apply_update(update)
84 }
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
88#[serde(bound = "S: StateSchema")]
89pub struct StateUpdate<S: StateSchema> {
90 pub data: S::Update,
91}
92#[cfg(test)]
93#[path = "state_tests.rs"]
94mod state_tests;
95impl<S: StateSchema> StateUpdate<S> {
96 pub fn new(data: S::Update) -> Self {
97 Self { data }
98 }
99}