edfsm_kv_store/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3
4pub mod path;
5pub use path::Path;
6
7#[cfg(feature = "tokio")]
8pub mod async_query;
9#[cfg(feature = "tokio")]
10pub use async_query::{requester, Requester};
11
12extern crate alloc;
13use alloc::{
14    boxed::Box,
15    collections::{btree_map::Entry, BTreeMap},
16};
17use core::{clone::Clone, ops::Bound};
18use edfsm::{Change, Drain, Fsm, Init, Input, Terminating};
19use serde::{Deserialize, Serialize};
20
21/// The event type of an Fsm
22pub type Event<M> = <M as Fsm>::E;
23
24/// The command type of an Fsm
25pub type Command<M> = <M as Fsm>::C;
26
27/// The input type of an Fsm
28pub type In<M> = Input<<M as Fsm>::C, <M as Fsm>::E>;
29
30/// The output message type of an Fsm for the purpose of this module.
31pub type Out<M> = <<M as Fsm>::SE as Drain>::Item;
32
33/// The effector/effects type of an Fsm
34pub type Effect<M> = <M as Fsm>::SE;
35
36/// The state type of an Fsm
37pub type State<M> = <M as Fsm>::S;
38
39/// A query to the KV store.
40///
41/// Type parameter `V` is the value type of the KV store. (The key type is `Path`.)
42/// The parameter `E` is type for events that can update a value. In other words,
43/// the inner state machine at each Path receives events type `E` and manages state type `V`.
44///
45/// The functions of type `RespondOne` and `RespondMany` are passed the query result.
46/// Results contain borrowed values `&V` which can't be passed to channels or
47/// other data structures.  The respond function may clone these to pass them on,
48/// or the function may interpret or aggregate borrowed values in place.
49///
50pub enum Query<V, E> {
51    /// Get the value at the given path, or None.
52    Get(Path, RespondOne<V, ()>),
53
54    /// Get the entries whose path starts with the given path,
55    /// including the entry for the path itself.
56    GetTree(Path, RespondMany<V, ()>),
57
58    /// Get the entries in the given range
59    GetRange((Bound<Path>, Bound<Path>), RespondMany<V, ()>),
60
61    /// Get all the entries
62    GetAll(RespondMany<V, ()>),
63
64    /// Get the value at the given path or None and emit an event for that path.
65    Upsert(Path, RespondOne<V, E>),
66
67    /// Get all the entries and emit an event for a particular (usually new) path.
68    Insert(RespondMany<V, Keyed<E>>),
69}
70
71/// Type of a function that will respond to an many-valued query.
72pub type RespondMany<V, E> = Box<dyn FnOnce(&mut dyn Iterator<Item = (&Path, &V)>) -> E + Send>;
73
74/// Type of a function that will respond to a single valued query.
75pub type RespondOne<V, E> = Box<dyn FnOnce(Option<&V>) -> E + Send>;
76
77/// `KvStore<M>` represents the collection of state machines of type `M`.
78///
79/// `KvStore<M>` implements `Fsm` by distributing events to
80/// the machines in contains by key.
81/// The event type must implement trait `Keyed` which provides a key
82/// for each event or type `Path`.
83///
84/// Commands are used to query and manager the store.  
85pub struct KvStore<M>(BTreeMap<Path, State<M>>)
86where
87    M: Fsm;
88
89impl<M> Fsm for KvStore<M>
90where
91    M: Fsm + 'static,
92    State<M>: Default,
93    Event<M>: Terminating,
94    Effect<M>: Drain,
95{
96    type S = Self;
97    type C = Query<State<M>, Event<M>>;
98    type E = Keyed<Event<M>>;
99    type SE = Keyed<Effect<M>>;
100
101    fn for_command(store: &Self::S, command: Self::C, _se: &mut Self::SE) -> Option<Self::E> {
102        use Bound::*;
103        use Query::*;
104        match command {
105            Get(path, respond) => {
106                respond(store.0.get(&path));
107                None
108            }
109            GetTree(path, respond) => {
110                respond(
111                    &mut (store
112                        .0
113                        .range((Included(&path), Unbounded))
114                        .take_while(|(p, _)| p.len() > path.len() || *p == &path)),
115                );
116                None
117            }
118            GetRange(bounds, respond) => {
119                respond(&mut store.0.range(bounds));
120                None
121            }
122            GetAll(respond) => {
123                respond(&mut store.0.iter());
124                None
125            }
126            Upsert(path, respond) => {
127                let e = respond(store.0.get(&path));
128                Some(Keyed { key: path, item: e })
129            }
130            Insert(respond) => {
131                let e = respond(&mut store.0.iter());
132                Some(e)
133            }
134        }
135    }
136
137    fn on_event(r: &mut Self::S, e: &Self::E) -> Option<Change> {
138        use Entry::*;
139        match (r.0.entry(e.key.clone()), e.item.terminating()) {
140            (Occupied(entry), false) => {
141                let s = entry.into_mut();
142                M::on_event(s, &e.item)
143            }
144            (Vacant(entry), false) => {
145                let s = entry.insert(Default::default());
146                M::on_event(s, &e.item)
147            }
148            (Occupied(entry), true) => {
149                entry.remove();
150                Some(Change::Transitioned)
151            }
152            (Vacant(_), true) => None,
153        }
154    }
155
156    fn on_change(r: &Self::S, e: &Self::E, se: &mut Self::SE, change: Change) {
157        if let Some(s) = r.0.get(&e.key) {
158            se.key = e.key.clone();
159            M::on_change(s, &e.item, &mut se.item, change);
160        }
161    }
162}
163
164/// This type pairs a `Path` with another value.
165/// This may be an event or output of a state machine
166/// in the KvStore.
167#[derive(Clone, Debug, Serialize, Deserialize)]
168pub struct Keyed<A> {
169    pub key: Path,
170    pub item: A,
171}
172
173impl<M> Default for KvStore<M>
174where
175    M: Fsm,
176{
177    fn default() -> Self {
178        Self(BTreeMap::new())
179    }
180}
181
182impl<SE> Drain for Keyed<SE>
183where
184    SE: Drain,
185{
186    type Item = Keyed<SE::Item>;
187
188    fn drain_all(&mut self) -> impl Iterator<Item = Self::Item> + Send {
189        self.item.drain_all().map(|item| Keyed {
190            key: self.key.clone(),
191            item,
192        })
193    }
194}
195
196impl<A> Terminating for Keyed<A> {
197    fn terminating(&self) -> bool {
198        false
199    }
200}
201
202impl<S, SE> Init<S> for Keyed<SE> {
203    fn init(&mut self, _state: &S) {}
204}
205
206impl<SE> Default for Keyed<SE>
207where
208    SE: Default,
209{
210    fn default() -> Self {
211        Self {
212            key: Default::default(),
213            item: Default::default(),
214        }
215    }
216}