mock_store/
store.rs

1use crate::{Error, Result};
2use modql::filter::FilterGroups;
3use serde::de::DeserializeOwned;
4use serde::Serialize;
5use serde_json::{from_value, to_value, Value};
6use std::any::TypeId;
7use std::collections::HashMap;
8use std::sync::atomic::{AtomicU64, Ordering};
9use std::sync::{Arc, Mutex};
10
11#[derive(Clone, Default)]
12pub struct Store {
13	stores: Arc<Mutex<HashMap<TypeId, ObjectStore>>>,
14}
15
16impl Store {
17	/// Same as Store::default()
18	pub fn new() -> Self {
19		Self::default()
20	}
21}
22
23// region:    --- Store Public Interface
24impl Store {
25	/// Each object store have a u64 sequential id generator which will increment on each call.
26	/// Notes:
27	///   - The first `seq_next()` call will return `1` since is initialize at 0.
28	///   - Also, if the object store does it exists yet, it will create it.
29	pub fn seq_next<T: Serialize + 'static>(&self) -> u64 {
30		self.get_or_create_obj_store::<T>().next_seq()
31	}
32
33	/// Insert a new value to the underlying store for this type.
34	///
35	/// Fails if it cannot call `serde_json::to_value`
36	pub fn insert<T: Serialize + 'static>(&self, val: T) -> Result<()> {
37		// -- Get or create the object store.
38		let obj_store = self.get_or_create_obj_store::<T>();
39
40		// -- Convert to json value.
41		let val = to_value(val)?;
42
43		// -- Insert to object store.
44		obj_store.insert(val);
45
46		Ok(())
47	}
48
49	pub fn first<T>(&self, filter: impl Into<Option<FilterGroups>>) -> Result<Option<T>>
50	where
51		T: DeserializeOwned + 'static,
52	{
53		// -- Get the object store.
54		let obj_store = self.get_obj_store::<T>();
55
56		// -- If no store, return empty array.
57		let Some(obj_store) = obj_store else {
58			return Ok(None);
59		};
60
61		// -- Perform the list and filter if object store.
62		let filter_groups = filter.into();
63
64		let res = obj_store.first(&filter_groups).map(|v| from_value::<T>(v)).transpose();
65
66		Ok(res?)
67	}
68
69	/// List all of the values for a given Type application the Filter
70	pub fn list<T>(&self, filter: impl Into<Option<FilterGroups>>) -> Result<Vec<T>>
71	where
72		T: DeserializeOwned + 'static,
73	{
74		// -- Get the object store.
75		let obj_store = self.get_obj_store::<T>();
76
77		// -- If no store, return empty array.
78		let Some(obj_store) = obj_store else {
79			return Ok(Vec::new())
80		};
81
82		// -- Perform the list and filter if object store.
83		let filter_groups = filter.into();
84		let objects: std::result::Result<Vec<T>, serde_json::Error> =
85			obj_store.list(&filter_groups).into_iter().map(|v| from_value::<T>(v)).collect();
86
87		Ok(objects?)
88	}
89
90	pub fn delete<T>(&self, filter: impl Into<FilterGroups>) -> Result<u64>
91	where
92		T: DeserializeOwned + 'static,
93	{
94		// -- Get the object store.
95		let obj_store = self.get_obj_store::<T>().ok_or(Error::FailToDeleteNoStoreForType)?;
96
97		// -- Perform the delete.
98		let filter_groups = filter.into();
99		let count = obj_store.delete(&filter_groups)?;
100
101		Ok(count)
102	}
103
104	/// Update zero or more item in a type store, for a given filter_groups with the modifier function.
105	///
106	/// Some limitations:
107	/// - It will few clones to keep the API ergonomic convenient. (might try to use `Deserialize<'_>`)
108	/// - For now, it will stop at first error. Eventually, we might capture the error an continue.
109	/// - Obviously, do not have commit/rollback capability. This is just a mock-store.
110	pub fn update<T, F>(&self, filter: impl Into<FilterGroups>, modifier: F) -> Result<u64>
111	where
112		T: Serialize + DeserializeOwned + 'static,
113		F: Fn(T) -> T,
114	{
115		// -- Get the object store.
116		let obj_store = self.get_obj_store::<T>().ok_or(Error::FailToUpdateNoStoreForType)?;
117
118		// -- Perform the update.
119		let filter_groups = filter.into();
120		let count = obj_store.update_raw(&filter_groups, |v| {
121			let obj = from_value::<T>(v.clone())?;
122			let obj = modifier(obj);
123			let new_v = to_value(obj)?;
124			*v = new_v;
125			Ok(())
126		})?;
127
128		Ok(count)
129	}
130
131	// region:    --- Privates
132	fn get_obj_store<T: 'static>(&self) -> Option<ObjectStore> {
133		let stores = self.stores.lock().unwrap();
134		let tid = TypeId::of::<T>();
135		stores.get(&tid).cloned()
136	}
137
138	fn get_or_create_obj_store<T: Serialize + 'static>(&self) -> ObjectStore {
139		let mut stores = self.stores.lock().unwrap();
140		let tid = TypeId::of::<T>();
141		stores.entry(tid).or_insert_with(ObjectStore::default).clone()
142	}
143	// endregion: --- Privates
144}
145// endregion: --- Store Public Interface
146
147// region:    --- ObjectStore
148#[derive(Clone, Default)]
149struct ObjectStore {
150	store: Arc<Mutex<Vec<Value>>>,
151	seq: Arc<AtomicU64>,
152}
153
154impl ObjectStore {
155	fn next_seq(&self) -> u64 {
156		self.seq.fetch_add(1, Ordering::Relaxed) + 1
157	}
158
159	fn insert(&self, val: Value) {
160		let mut store = self.store.lock().unwrap();
161		store.push(val);
162	}
163
164	fn first(&self, filter_groups: &Option<FilterGroups>) -> Option<Value> {
165		let store = self.store.lock().unwrap();
166
167		let filter_groups = filter_groups.as_ref();
168
169		store
170			.iter()
171			.find(|v| {
172				filter_groups
173					.map(|filter_groups| filter_groups.is_match_json(v))
174					.unwrap_or(true)
175			})
176			.cloned()
177	}
178
179	fn list(&self, filter_groups: &Option<FilterGroups>) -> Vec<Value> {
180		let store = self.store.lock().unwrap();
181
182		let filter_groups = filter_groups.as_ref();
183		store
184			.iter()
185			.filter(|v| {
186				filter_groups
187					.map(|filter_groups| filter_groups.is_match_json(v))
188					.unwrap_or(true)
189			})
190			.cloned()
191			.collect()
192	}
193
194	fn delete(&self, filter_groups: &FilterGroups) -> Result<u64> {
195		let mut store = self.store.lock().unwrap();
196
197		let mut count: u64 = 0;
198
199		// TODO: Need to optimize.
200		//   - Option 1: Get indexes and do swap_remove (remaining order not preserved)
201		//   - Option 2: Can use `drain_filter` when stable.
202		store.retain(|v| {
203			let retain = !filter_groups.is_match_json(v);
204			if !retain {
205				count += 1;
206			}
207			retain
208		});
209
210		Ok(count)
211	}
212
213	fn update_raw<F>(&self, filter_groups: &FilterGroups, raw_modifier: F) -> Result<u64>
214	where
215		F: Fn(&mut Value) -> Result<()>,
216	{
217		let mut store = self.store.lock().unwrap();
218
219		let mut count: u64 = 0;
220
221		for value in store.iter_mut() {
222			if filter_groups.is_match_json(value) {
223				raw_modifier(value)?;
224				count += 1;
225			}
226		}
227
228		Ok(count)
229	}
230}
231// endregion: --- ObjectStore