Skip to main content

eventsdb_core/
mem.rs

1//! The in-memory backend.
2//!
3//! One stream, owned by one handle in one process, which is what makes its
4//! writes serialized without a lock. It is the store a test uses and the
5//! store an ephemeral session uses; it has no database, so it answers no SQL
6//! and assigns no [`crate::position::Position`].
7
8use async_trait::async_trait;
9use serde_json::{Map, Value};
10
11use crate::error::Result;
12use crate::event::{now_ms, stamp, validate};
13use crate::position::Committed;
14use crate::store::{Decision, EventStore};
15use crate::upcast::{apply_chain, Current, UpcastChain};
16
17#[derive(Default)]
18pub struct MemEventStore {
19    stream_id: String,
20    events: Vec<Map<String, Value>>,
21    chain: UpcastChain,
22}
23
24impl MemEventStore {
25    pub fn new(stream_id: impl Into<String>) -> Self {
26        MemEventStore {
27            stream_id: stream_id.into(),
28            events: Vec::new(),
29            chain: UpcastChain::new(),
30        }
31    }
32
33    /// Register the upcaster chain applied to every read.
34    pub fn with_upcasters(mut self, chain: UpcastChain) -> Self {
35        self.chain = chain;
36        self
37    }
38
39    /// Read back through the chain, in `seq` order, filtered as `read_kinds`
40    /// filters.
41    fn project(&self, kinds: Option<&[&str]>, from_seq: u64, limit: usize) -> Result<Vec<Current>> {
42        let selected: Vec<Value> = self
43            .events
44            .iter()
45            .filter(|event| stored_seq(event) >= from_seq)
46            .filter(|event| match kinds {
47                None => true,
48                Some(kinds) => stored_kind(event).is_some_and(|k| kinds.contains(&k)),
49            })
50            .take(limit)
51            .map(|event| Value::Object(event.clone()))
52            .collect();
53        apply_chain(&self.chain, selected)
54            .into_iter()
55            .map(Current::from_upcasted)
56            .collect()
57    }
58
59    fn next_seq(&self) -> u64 {
60        self.events.len() as u64 + 1
61    }
62}
63
64#[async_trait]
65impl EventStore for MemEventStore {
66    fn stream_id(&self) -> &str {
67        &self.stream_id
68    }
69
70    async fn append(&mut self, event: Map<String, Value>) -> Result<Committed> {
71        let seq = self.next_seq();
72        let epoch_ms = now_ms();
73        let stamped = stamp(event, seq, epoch_ms)?;
74        self.events.push(stamped);
75        Ok(Committed {
76            seq,
77            epoch_ms,
78            position: None,
79        })
80    }
81
82    /// Validates the whole batch before writing any of it, which is the only
83    /// way this backend's writes fail — so all-or-nothing holds here too.
84    async fn append_many(&mut self, events: Vec<Map<String, Value>>) -> Result<Vec<Committed>> {
85        for event in &events {
86            validate(event)?;
87        }
88        let mut committed = Vec::with_capacity(events.len());
89        for event in events {
90            committed.push(self.append(event).await?);
91        }
92        Ok(committed)
93    }
94
95    async fn append_if(
96        &mut self,
97        kinds: Option<&[&str]>,
98        decide: Decision,
99    ) -> Result<Option<Committed>> {
100        let seen = self.project(kinds, 0, usize::MAX)?;
101        match decide(&seen) {
102            None => Ok(None),
103            Some(event) => self.append(event).await.map(Some),
104        }
105    }
106
107    async fn read_kinds(
108        &self,
109        kinds: Option<&[&str]>,
110        from_seq: u64,
111        limit: usize,
112    ) -> Result<Vec<Current>> {
113        self.project(kinds, from_seq, limit)
114    }
115
116    async fn read_last(&self, n: usize) -> Result<Vec<Current>> {
117        let from = self.events.len().saturating_sub(n);
118        let selected: Vec<Value> = self.events[from..]
119            .iter()
120            .map(|event| Value::Object(event.clone()))
121            .collect();
122        apply_chain(&self.chain, selected)
123            .into_iter()
124            .map(Current::from_upcasted)
125            .collect()
126    }
127
128    async fn head(&self) -> Result<Option<u64>> {
129        Ok(self.events.last().map(stored_seq))
130    }
131
132    async fn len(&self) -> Result<usize> {
133        Ok(self.events.len())
134    }
135}
136
137fn stored_seq(event: &Map<String, Value>) -> u64 {
138    event
139        .get(crate::event::FIELD_SEQ)
140        .and_then(Value::as_u64)
141        .expect("every stored event was stamped")
142}
143
144fn stored_kind(event: &Map<String, Value>) -> Option<&str> {
145    event.get(crate::event::FIELD_KIND).and_then(Value::as_str)
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151    use serde_json::json;
152
153    fn object(value: Value) -> Map<String, Value> {
154        value
155            .as_object()
156            .expect("test literal is an object")
157            .clone()
158    }
159
160    fn event(kind: &str) -> Map<String, Value> {
161        object(json!({ "kind": kind }))
162    }
163
164    #[tokio::test]
165    async fn seq_starts_at_one_and_increases() {
166        let mut store = MemEventStore::new("s");
167        assert_eq!(store.append(event("a")).await.unwrap().seq, 1);
168        assert_eq!(store.append(event("b")).await.unwrap().seq, 2);
169        assert_eq!(store.head().await.unwrap(), Some(2));
170        assert_eq!(store.len().await.unwrap(), 2);
171    }
172
173    #[tokio::test]
174    async fn a_rejected_event_consumes_no_sequence_number() {
175        let mut store = MemEventStore::new("s");
176        store.append(event("a")).await.unwrap();
177        assert!(store.append(object(json!({ "nope": 1 }))).await.is_err());
178        assert_eq!(store.append(event("b")).await.unwrap().seq, 2);
179    }
180
181    #[tokio::test]
182    async fn a_batch_that_fails_validation_writes_none_of_itself() {
183        let mut store = MemEventStore::new("s");
184        let batch = vec![event("a"), object(json!({ "nope": 1 }))];
185        assert!(store.append_many(batch).await.is_err());
186        assert_eq!(store.len().await.unwrap(), 0);
187    }
188
189    #[tokio::test]
190    async fn reads_filter_by_kind_and_limit_counts_what_came_back() {
191        let mut store = MemEventStore::new("s");
192        for kind in ["a", "b", "a", "b", "a"] {
193            store.append(event(kind)).await.unwrap();
194        }
195        let read = store.read_kinds(Some(&["a"]), 0, 2).await.unwrap();
196        assert_eq!(read.len(), 2);
197        assert!(read.iter().all(|e| e.kind() == "a"));
198        assert_eq!(read[0].seq(), 1);
199        assert_eq!(read[1].seq(), 3);
200    }
201
202    #[tokio::test]
203    async fn an_empty_kind_slice_selects_nothing() {
204        let mut store = MemEventStore::new("s");
205        store.append(event("a")).await.unwrap();
206        assert!(store.read_kinds(Some(&[]), 0, 10).await.unwrap().is_empty());
207    }
208
209    #[tokio::test]
210    async fn a_decision_sees_the_filtered_stream_and_may_write_nothing() {
211        let mut store = MemEventStore::new("s");
212        store.append(event("a")).await.unwrap();
213        store.append(event("b")).await.unwrap();
214
215        let refused = store
216            .append_if(
217                Some(&["a"]),
218                Box::new(|seen| {
219                    assert_eq!(seen.len(), 1);
220                    assert_eq!(seen[0].kind(), "a");
221                    None
222                }),
223            )
224            .await
225            .unwrap();
226        assert!(refused.is_none());
227        assert_eq!(store.len().await.unwrap(), 2);
228
229        let written = store
230            .append_if(
231                None,
232                Box::new(|seen| {
233                    Some(object(
234                        json!({ "kind": "c", "data": { "saw": seen.len() } }),
235                    ))
236                }),
237            )
238            .await
239            .unwrap();
240        assert_eq!(written.unwrap().seq, 3);
241    }
242
243    #[tokio::test]
244    async fn read_last_returns_the_end_in_seq_order() {
245        let mut store = MemEventStore::new("s");
246        for kind in ["a", "b", "c"] {
247            store.append(event(kind)).await.unwrap();
248        }
249        let last = store.read_last(2).await.unwrap();
250        assert_eq!(last.len(), 2);
251        assert_eq!(last[0].kind(), "b");
252        assert_eq!(last[1].kind(), "c");
253    }
254
255    #[tokio::test]
256    async fn this_backend_assigns_no_global_position() {
257        let mut store = MemEventStore::new("s");
258        assert!(store.append(event("a")).await.unwrap().position.is_none());
259    }
260}