initiative_core/storage/
data_store.rs

1use crate::utils::CaseInsensitiveStr;
2use crate::{Thing, Uuid};
3use async_trait::async_trait;
4use std::cell::RefCell;
5use std::collections::hash_map::Entry;
6use std::collections::HashMap;
7use std::rc::Rc;
8
9#[derive(Default)]
10pub struct NullDataStore;
11
12#[cfg_attr(test, derive(Debug, PartialEq))]
13#[derive(Clone, Default)]
14pub struct MemoryDataStore {
15    pub things: Rc<RefCell<HashMap<Uuid, Thing>>>,
16    pub key_values: Rc<RefCell<HashMap<String, String>>>,
17}
18
19#[async_trait(?Send)]
20impl DataStore for NullDataStore {
21    async fn health_check(&self) -> Result<(), ()> {
22        Err(())
23    }
24
25    async fn delete_thing_by_uuid(&mut self, _uuid: &Uuid) -> Result<(), ()> {
26        Err(())
27    }
28
29    async fn edit_thing(&mut self, _thing: &Thing) -> Result<(), ()> {
30        Err(())
31    }
32
33    async fn get_all_the_things(&self) -> Result<Vec<Thing>, ()> {
34        Err(())
35    }
36
37    async fn get_thing_by_uuid(&self, _uuid: &Uuid) -> Result<Option<Thing>, ()> {
38        Err(())
39    }
40
41    async fn get_thing_by_name(&self, _name: &str) -> Result<Option<Thing>, ()> {
42        Err(())
43    }
44
45    async fn get_things_by_name_start(
46        &self,
47        _name: &str,
48        _limit: Option<usize>,
49    ) -> Result<Vec<Thing>, ()> {
50        Err(())
51    }
52
53    async fn save_thing(&mut self, _thing: &Thing) -> Result<(), ()> {
54        Err(())
55    }
56
57    async fn set_value(&mut self, _key: &str, _value: &str) -> Result<(), ()> {
58        Err(())
59    }
60
61    async fn get_value(&self, _key: &str) -> Result<Option<String>, ()> {
62        Err(())
63    }
64
65    async fn delete_value(&mut self, _key: &str) -> Result<(), ()> {
66        Err(())
67    }
68}
69
70impl MemoryDataStore {
71    pub fn snapshot(&self) -> (HashMap<Uuid, Thing>, HashMap<String, String>) {
72        (
73            self.things.borrow().clone(),
74            self.key_values.borrow().clone(),
75        )
76    }
77}
78
79#[async_trait(?Send)]
80impl DataStore for MemoryDataStore {
81    async fn health_check(&self) -> Result<(), ()> {
82        Ok(())
83    }
84
85    async fn delete_thing_by_uuid(&mut self, uuid: &Uuid) -> Result<(), ()> {
86        self.things.borrow_mut().remove(uuid).map(|_| ()).ok_or(())
87    }
88
89    async fn edit_thing(&mut self, thing: &Thing) -> Result<(), ()> {
90        self.things
91            .borrow_mut()
92            .entry(thing.uuid)
93            .and_modify(|t| *t = thing.clone())
94            .or_insert_with(|| thing.clone());
95        Ok(())
96    }
97
98    async fn get_all_the_things(&self) -> Result<Vec<Thing>, ()> {
99        Ok(self.things.borrow().values().cloned().collect())
100    }
101
102    async fn get_thing_by_uuid(&self, uuid: &Uuid) -> Result<Option<Thing>, ()> {
103        Ok(self.things.borrow().get(uuid).cloned())
104    }
105
106    async fn get_thing_by_name(&self, name: &str) -> Result<Option<Thing>, ()> {
107        Ok(self
108            .things
109            .borrow()
110            .values()
111            .find(|thing| thing.name().value().is_some_and(|s| s.eq_ci(name)))
112            .cloned())
113    }
114
115    async fn get_things_by_name_start(
116        &self,
117        name: &str,
118        limit: Option<usize>,
119    ) -> Result<Vec<Thing>, ()> {
120        Ok(self
121            .things
122            .borrow()
123            .values()
124            .filter(|thing| thing.name().value().is_some_and(|s| s.starts_with_ci(name)))
125            .take(limit.unwrap_or(usize::MAX))
126            .cloned()
127            .collect())
128    }
129
130    async fn save_thing(&mut self, thing: &Thing) -> Result<(), ()> {
131        let mut things = self.things.borrow_mut();
132
133        if let Entry::Vacant(e) = things.entry(thing.uuid) {
134            e.insert(thing.clone());
135            Ok(())
136        } else {
137            Err(())
138        }
139    }
140
141    async fn set_value(&mut self, key: &str, value: &str) -> Result<(), ()> {
142        let mut key_values = self.key_values.borrow_mut();
143        key_values.insert(key.to_string(), value.to_string());
144        Ok(())
145    }
146
147    async fn get_value(&self, key: &str) -> Result<Option<String>, ()> {
148        let key_values = self.key_values.borrow();
149        Ok(key_values.get(key).cloned())
150    }
151
152    async fn delete_value(&mut self, key: &str) -> Result<(), ()> {
153        let mut key_values = self.key_values.borrow_mut();
154        key_values.remove(key);
155        Ok(())
156    }
157}
158
159#[async_trait(?Send)]
160pub trait DataStore {
161    async fn health_check(&self) -> Result<(), ()>;
162
163    async fn delete_thing_by_uuid(&mut self, uuid: &Uuid) -> Result<(), ()>;
164
165    async fn edit_thing(&mut self, thing: &Thing) -> Result<(), ()>;
166
167    async fn get_all_the_things(&self) -> Result<Vec<Thing>, ()>;
168
169    async fn get_thing_by_uuid(&self, uuid: &Uuid) -> Result<Option<Thing>, ()>;
170
171    async fn get_thing_by_name(&self, name: &str) -> Result<Option<Thing>, ()>;
172
173    async fn get_things_by_name_start(
174        &self,
175        name: &str,
176        limit: Option<usize>,
177    ) -> Result<Vec<Thing>, ()>;
178
179    async fn save_thing(&mut self, thing: &Thing) -> Result<(), ()>;
180
181    async fn set_value(&mut self, key: &str, value: &str) -> Result<(), ()>;
182
183    async fn get_value(&self, key: &str) -> Result<Option<String>, ()>;
184
185    async fn delete_value(&mut self, key: &str) -> Result<(), ()>;
186}
187
188#[cfg(test)]
189mod test {
190    use super::*;
191    use crate::test_utils as test;
192
193    #[tokio::test]
194    async fn memory_delete_thing_by_uuid_test() {
195        let mut ds = test::data_store::memory::with_test_data();
196        let len = ds.things.borrow().len();
197
198        assert_eq!(
199            Err(()),
200            ds.delete_thing_by_uuid(&test::thing::ODYSSEUS).await
201        );
202        assert_eq!(len, ds.things.borrow().len());
203        assert_eq!(
204            Ok(()),
205            ds.delete_thing_by_uuid(&test::thing::PENELOPE).await
206        );
207        assert_eq!(len - 1, ds.things.borrow().len());
208    }
209
210    #[tokio::test]
211    async fn memory_get_thing_by_uuid_test() {
212        let ds = test::data_store::memory::with_test_data();
213
214        assert_eq!(Ok(None), ds.get_thing_by_uuid(&test::thing::ODYSSEUS).await);
215        assert_eq!(
216            Ok(Some(test::thing::penelope())),
217            ds.get_thing_by_uuid(&test::thing::PENELOPE).await,
218        );
219    }
220
221    #[tokio::test]
222    async fn memory_get_thing_by_name_test() {
223        let ds = test::data_store::memory::with_test_data();
224
225        assert_eq!(Ok(None), ds.get_thing_by_name("odysseus").await);
226        assert_eq!(
227            Ok(Some(test::thing::penelope())),
228            ds.get_thing_by_name("penelope").await,
229        );
230    }
231
232    #[tokio::test]
233    async fn memory_get_things_by_name_start_test() {
234        let ds = test::data_store::memory::with_test_data();
235
236        let mut results = ds.get_things_by_name_start("p", None).await.unwrap();
237        results.sort_by_key(|thing| thing.uuid);
238        assert_eq!(
239            vec![test::thing::phoenicia(), test::thing::penelope()],
240            results,
241        );
242
243        assert_eq!(
244            1,
245            ds.get_things_by_name_start("p", Some(1))
246                .await
247                .unwrap()
248                .len(),
249        );
250    }
251
252    #[tokio::test]
253    async fn memory_edit_thing_test() {
254        let mut ds = test::data_store::memory();
255
256        let odysseus = test::thing::odysseus();
257        let nobody = test::npc().name("Nobody").build_thing(test::npc::ODYSSEUS);
258
259        assert_eq!(Ok(()), ds.edit_thing(&odysseus).await);
260        assert_eq!(1, ds.things.borrow().len());
261        assert_eq!(Ok(()), ds.edit_thing(&nobody).await);
262        assert_eq!(1, ds.things.borrow().len());
263        assert_eq!(
264            "Nobody",
265            ds.things
266                .borrow()
267                .get(&test::npc::ODYSSEUS)
268                .unwrap()
269                .name()
270                .to_string(),
271        );
272    }
273
274    #[tokio::test]
275    async fn memory_get_all_the_things_test() {
276        let ds =
277            test::data_store::memory::with([test::thing::odysseus(), test::thing::penelope()], []);
278
279        let all_the_things = ds.get_all_the_things().await.unwrap();
280        assert_eq!(2, all_the_things.len(), "{all_the_things:?}");
281    }
282
283    #[tokio::test]
284    async fn memory_save_thing_test() {
285        let mut ds = test::data_store::memory();
286
287        assert_eq!(Ok(()), ds.save_thing(&test::thing::odysseus()).await);
288        assert_eq!(
289            Err(()),
290            ds.save_thing(&test::npc().build_thing(test::thing::ODYSSEUS))
291                .await,
292        );
293
294        assert_eq!(Ok(1), ds.get_all_the_things().await.map(|v| v.len()));
295    }
296
297    #[tokio::test]
298    async fn memory_key_value_test() {
299        let mut ds = test::data_store::memory();
300
301        assert_eq!(Ok(()), ds.set_value("somekey", "abc").await);
302        assert_eq!(Ok(()), ds.set_value("otherkey", "def").await);
303        assert_eq!(Ok(()), ds.set_value("somekey", "xyz").await);
304        assert_eq!(Ok(None), ds.get_value("notakey").await);
305        assert_eq!(Ok(Some("xyz".to_string())), ds.get_value("somekey").await);
306        assert_eq!(Ok(()), ds.delete_value("somekey").await);
307        assert_eq!(Ok(None), ds.get_value("somekey").await);
308    }
309
310    #[tokio::test]
311    async fn memory_clone_test() {
312        let mut ds1 = test::data_store::memory();
313        let ds2 = ds1.clone();
314
315        assert_eq!(Ok(()), ds1.set_value("somekey", "abc").await);
316        assert_eq!(Ok(Some("abc".to_string())), ds2.get_value("somekey").await);
317
318        assert_eq!(Ok(()), ds1.save_thing(&test::thing::odysseus()).await);
319        assert_eq!(
320            Ok(Some(test::thing::odysseus())),
321            ds2.get_thing_by_uuid(&test::thing::ODYSSEUS).await,
322        );
323    }
324}