1use std::collections::HashMap;
8
9use frp_plexus::{AtomId, BlockId, EdgeId};
10
11use crate::error::StoreError;
12use crate::query::{Query, QueryResult};
13use crate::store::{AtomStore, BlockStore, EdgeStore};
14
15fn paginate<'a, T>(
20 iter: impl Iterator<Item = &'a T>,
21 total: usize,
22 query: &Query,
23) -> QueryResult<&'a T> {
24 let paged: Vec<&T> = iter
25 .skip(query.offset)
26 .take(query.limit.unwrap_or(usize::MAX))
27 .collect();
28 QueryResult::new(paged, total, query.offset)
29}
30
31#[derive(Debug, Default)]
41pub struct InMemoryAtomStore<V: Clone> {
42 data: HashMap<AtomId, V>,
43}
44
45impl<V: Clone> InMemoryAtomStore<V> {
46 pub fn new() -> Self {
48 Self { data: HashMap::new() }
49 }
50
51 pub fn len(&self) -> usize {
53 self.data.len()
54 }
55
56 pub fn is_empty(&self) -> bool {
58 self.data.is_empty()
59 }
60}
61
62pub trait HasAtomId {
65 fn atom_id(&self) -> AtomId;
66}
67
68impl<V: Clone + HasAtomId> AtomStore for InMemoryAtomStore<V> {
69 type Atom = V;
70
71 fn get_atom(&self, id: AtomId) -> Result<&V, StoreError> {
72 self.data.get(&id).ok_or_else(|| StoreError::not_found(id))
73 }
74
75 fn put_atom(&mut self, atom: V) -> Result<(), StoreError> {
76 let id = atom.atom_id();
77 self.data.insert(id, atom);
78 Ok(())
79 }
80
81 fn delete_atom(&mut self, id: AtomId) -> Result<(), StoreError> {
82 self.data.remove(&id).ok_or_else(|| StoreError::not_found(id))?;
83 Ok(())
84 }
85
86 fn query_atoms(&self, query: &Query) -> Result<QueryResult<&V>, StoreError> {
87 let all: Vec<&V> = self.data.values().collect();
88 let total = all.len();
89 Ok(paginate(all.into_iter(), total, query))
90 }
91}
92
93#[derive(Debug, Default)]
99pub struct InMemoryBlockStore<V: Clone> {
100 data: HashMap<BlockId, V>,
101}
102
103impl<V: Clone> InMemoryBlockStore<V> {
104 pub fn new() -> Self {
106 Self { data: HashMap::new() }
107 }
108
109 pub fn len(&self) -> usize {
111 self.data.len()
112 }
113
114 pub fn is_empty(&self) -> bool {
116 self.data.is_empty()
117 }
118}
119
120pub trait HasBlockId {
122 fn block_id(&self) -> BlockId;
123}
124
125impl<V: Clone + HasBlockId> BlockStore for InMemoryBlockStore<V> {
126 type Block = V;
127
128 fn get_block(&self, id: BlockId) -> Result<&V, StoreError> {
129 self.data.get(&id).ok_or_else(|| StoreError::not_found(id))
130 }
131
132 fn put_block(&mut self, block: V) -> Result<(), StoreError> {
133 let id = block.block_id();
134 self.data.insert(id, block);
135 Ok(())
136 }
137
138 fn delete_block(&mut self, id: BlockId) -> Result<(), StoreError> {
139 self.data.remove(&id).ok_or_else(|| StoreError::not_found(id))?;
140 Ok(())
141 }
142
143 fn query_blocks(&self, query: &Query) -> Result<QueryResult<&V>, StoreError> {
144 let all: Vec<&V> = self.data.values().collect();
145 let total = all.len();
146 Ok(paginate(all.into_iter(), total, query))
147 }
148}
149
150#[derive(Debug, Default)]
156pub struct InMemoryEdgeStore<V: Clone> {
157 data: HashMap<EdgeId, V>,
158}
159
160impl<V: Clone> InMemoryEdgeStore<V> {
161 pub fn new() -> Self {
163 Self { data: HashMap::new() }
164 }
165
166 pub fn len(&self) -> usize {
168 self.data.len()
169 }
170
171 pub fn is_empty(&self) -> bool {
173 self.data.is_empty()
174 }
175}
176
177pub trait HasEdgeId {
179 fn edge_id(&self) -> EdgeId;
180}
181
182impl<V: Clone + HasEdgeId> EdgeStore for InMemoryEdgeStore<V> {
183 type Edge = V;
184
185 fn get_edge(&self, id: EdgeId) -> Result<&V, StoreError> {
186 self.data.get(&id).ok_or_else(|| StoreError::not_found(id))
187 }
188
189 fn put_edge(&mut self, edge: V) -> Result<(), StoreError> {
190 let id = edge.edge_id();
191 self.data.insert(id, edge);
192 Ok(())
193 }
194
195 fn delete_edge(&mut self, id: EdgeId) -> Result<(), StoreError> {
196 self.data.remove(&id).ok_or_else(|| StoreError::not_found(id))?;
197 Ok(())
198 }
199
200 fn query_edges(&self, query: &Query) -> Result<QueryResult<&V>, StoreError> {
201 let all: Vec<&V> = self.data.values().collect();
202 let total = all.len();
203 Ok(paginate(all.into_iter(), total, query))
204 }
205}
206
207#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[derive(Debug, Clone, PartialEq)]
218 struct TestAtom {
219 id: AtomId,
220 name: &'static str,
221 }
222 impl HasAtomId for TestAtom {
223 fn atom_id(&self) -> AtomId { self.id }
224 }
225
226 #[derive(Debug, Clone, PartialEq)]
227 struct TestBlock {
228 id: BlockId,
229 }
230 impl HasBlockId for TestBlock {
231 fn block_id(&self) -> BlockId { self.id }
232 }
233
234 #[derive(Debug, Clone, PartialEq)]
235 struct TestEdge {
236 id: EdgeId,
237 }
238 impl HasEdgeId for TestEdge {
239 fn edge_id(&self) -> EdgeId { self.id }
240 }
241
242 #[test]
245 fn atom_put_and_get() {
246 let mut store = InMemoryAtomStore::new();
247 let id = AtomId::new(1);
248 store.put_atom(TestAtom { id, name: "a" }).unwrap();
249 assert_eq!(store.get_atom(id).unwrap().name, "a");
250 }
251
252 #[test]
253 fn atom_get_missing_returns_not_found() {
254 let store: InMemoryAtomStore<TestAtom> = InMemoryAtomStore::new();
255 let err = store.get_atom(AtomId::new(99)).unwrap_err();
256 assert!(matches!(err, StoreError::NotFound { .. }));
257 }
258
259 #[test]
260 fn atom_delete_removes_entry() {
261 let mut store = InMemoryAtomStore::new();
262 let id = AtomId::new(1);
263 store.put_atom(TestAtom { id, name: "x" }).unwrap();
264 store.delete_atom(id).unwrap();
265 assert!(store.get_atom(id).is_err());
266 }
267
268 #[test]
269 fn atom_delete_missing_returns_not_found() {
270 let mut store: InMemoryAtomStore<TestAtom> = InMemoryAtomStore::new();
271 assert!(matches!(
272 store.delete_atom(AtomId::new(5)).unwrap_err(),
273 StoreError::NotFound { .. }
274 ));
275 }
276
277 #[test]
278 fn atom_query_pagination() {
279 let mut store = InMemoryAtomStore::new();
280 for i in 0..5u64 {
281 store.put_atom(TestAtom { id: AtomId::new(i), name: "x" }).unwrap();
282 }
283 let q = Query::new().limit(2).offset(1);
284 let result = store.query_atoms(&q).unwrap();
285 assert_eq!(result.total, 5);
286 assert_eq!(result.items.len(), 2);
287 assert_eq!(result.offset, 1);
288 }
289
290 #[test]
293 fn block_put_get_delete() {
294 let mut store = InMemoryBlockStore::new();
295 let id = BlockId::new(10);
296 store.put_block(TestBlock { id }).unwrap();
297 assert_eq!(store.get_block(id).unwrap().id, id);
298 store.delete_block(id).unwrap();
299 assert!(store.get_block(id).is_err());
300 }
301
302 #[test]
305 fn edge_put_get_delete() {
306 let mut store = InMemoryEdgeStore::new();
307 let id = EdgeId::new(20);
308 store.put_edge(TestEdge { id }).unwrap();
309 assert_eq!(store.get_edge(id).unwrap().id, id);
310 store.delete_edge(id).unwrap();
311 assert!(store.get_edge(id).is_err());
312 }
313}