Skip to main content

common/database/
hashmap_store.rs

1// Generated by Qleany v1.7.3 from hashmap_store.tera
2
3//! HashMap-based storage backend with O(1) snapshots.
4//!
5//! Uses `im::HashMap` (persistent/immutable data structure with structural sharing)
6//! so that cloning the store for savepoints and undo is O(1).
7//! The store uses `RwLock` for interior mutability and thread safety,
8//! allowing `&HashMapStore` to be shared across threads via `Arc`.
9
10use crate::entities::*;
11use crate::snapshot::{StoreSnapshot, StoreSnapshotTrait};
12use crate::types::EntityId;
13use im::HashMap;
14use std::sync::RwLock;
15
16// ─────────────────────────────────────────────────────────────────────────────
17// The Store
18// ─────────────────────────────────────────────────────────────────────────────
19
20#[derive(Debug, Default)]
21pub struct HashMapStore {
22    // ── Entity tables ──────────────────────────────────────────────────
23    pub roots: RwLock<HashMap<EntityId, Root>>,
24    pub documents: RwLock<HashMap<EntityId, Document>>,
25    pub frames: RwLock<HashMap<EntityId, Frame>>,
26    pub blocks: RwLock<HashMap<EntityId, Block>>,
27    pub inline_elements: RwLock<HashMap<EntityId, InlineElement>>,
28    pub lists: RwLock<HashMap<EntityId, List>>,
29    pub resources: RwLock<HashMap<EntityId, Resource>>,
30    pub tables: RwLock<HashMap<EntityId, Table>>,
31    pub table_cells: RwLock<HashMap<EntityId, TableCell>>,
32
33    // ── Junction tables (one per forward relationship, shared for backward cleanup) ─
34    pub jn_document_from_root_document: RwLock<HashMap<EntityId, Vec<EntityId>>>,
35    pub jn_frame_from_document_frames: RwLock<HashMap<EntityId, Vec<EntityId>>>,
36    pub jn_list_from_document_lists: RwLock<HashMap<EntityId, Vec<EntityId>>>,
37    pub jn_resource_from_document_resources: RwLock<HashMap<EntityId, Vec<EntityId>>>,
38    pub jn_table_from_document_tables: RwLock<HashMap<EntityId, Vec<EntityId>>>,
39    pub jn_block_from_frame_blocks: RwLock<HashMap<EntityId, Vec<EntityId>>>,
40    pub jn_frame_from_frame_parent_frame: RwLock<HashMap<EntityId, Vec<EntityId>>>,
41    pub jn_table_from_frame_table: RwLock<HashMap<EntityId, Vec<EntityId>>>,
42    pub jn_inline_element_from_block_elements: RwLock<HashMap<EntityId, Vec<EntityId>>>,
43    pub jn_list_from_block_list: RwLock<HashMap<EntityId, Vec<EntityId>>>,
44    pub jn_table_cell_from_table_cells: RwLock<HashMap<EntityId, Vec<EntityId>>>,
45    pub jn_frame_from_table_cell_cell_frame: RwLock<HashMap<EntityId, Vec<EntityId>>>,
46
47    // ── ID counters (one per entity type) ──────────────────────────
48    pub counters: RwLock<std::collections::HashMap<String, EntityId>>,
49
50    // ── Savepoints ──────────────────────────────────────────────────
51    savepoints: RwLock<std::collections::HashMap<u64, HashMapStoreSnapshot>>,
52    next_savepoint_id: RwLock<u64>,
53}
54
55impl HashMapStore {
56    pub fn new() -> Self {
57        Self::default()
58    }
59
60    /// Clone the entire store for savepoint support. O(1) thanks to im::HashMap.
61    pub fn snapshot(&self) -> HashMapStoreSnapshot {
62        HashMapStoreSnapshot {
63            roots: self.roots.read().unwrap().clone(),
64            documents: self.documents.read().unwrap().clone(),
65            frames: self.frames.read().unwrap().clone(),
66            blocks: self.blocks.read().unwrap().clone(),
67            inline_elements: self.inline_elements.read().unwrap().clone(),
68            lists: self.lists.read().unwrap().clone(),
69            resources: self.resources.read().unwrap().clone(),
70            tables: self.tables.read().unwrap().clone(),
71            table_cells: self.table_cells.read().unwrap().clone(),
72            jn_document_from_root_document: self
73                .jn_document_from_root_document
74                .read()
75                .unwrap()
76                .clone(),
77            jn_frame_from_document_frames: self
78                .jn_frame_from_document_frames
79                .read()
80                .unwrap()
81                .clone(),
82            jn_list_from_document_lists: self.jn_list_from_document_lists.read().unwrap().clone(),
83            jn_resource_from_document_resources: self
84                .jn_resource_from_document_resources
85                .read()
86                .unwrap()
87                .clone(),
88            jn_table_from_document_tables: self
89                .jn_table_from_document_tables
90                .read()
91                .unwrap()
92                .clone(),
93            jn_block_from_frame_blocks: self.jn_block_from_frame_blocks.read().unwrap().clone(),
94            jn_frame_from_frame_parent_frame: self
95                .jn_frame_from_frame_parent_frame
96                .read()
97                .unwrap()
98                .clone(),
99            jn_table_from_frame_table: self.jn_table_from_frame_table.read().unwrap().clone(),
100            jn_inline_element_from_block_elements: self
101                .jn_inline_element_from_block_elements
102                .read()
103                .unwrap()
104                .clone(),
105            jn_list_from_block_list: self.jn_list_from_block_list.read().unwrap().clone(),
106            jn_table_cell_from_table_cells: self
107                .jn_table_cell_from_table_cells
108                .read()
109                .unwrap()
110                .clone(),
111            jn_frame_from_table_cell_cell_frame: self
112                .jn_frame_from_table_cell_cell_frame
113                .read()
114                .unwrap()
115                .clone(),
116            counters: self.counters.read().unwrap().clone(),
117        }
118    }
119
120    /// Restore from a savepoint snapshot.
121    pub fn restore(&self, snap: &HashMapStoreSnapshot) {
122        *self.roots.write().unwrap() = snap.roots.clone();
123        *self.documents.write().unwrap() = snap.documents.clone();
124        *self.frames.write().unwrap() = snap.frames.clone();
125        *self.blocks.write().unwrap() = snap.blocks.clone();
126        *self.inline_elements.write().unwrap() = snap.inline_elements.clone();
127        *self.lists.write().unwrap() = snap.lists.clone();
128        *self.resources.write().unwrap() = snap.resources.clone();
129        *self.tables.write().unwrap() = snap.tables.clone();
130        *self.table_cells.write().unwrap() = snap.table_cells.clone();
131        *self.jn_document_from_root_document.write().unwrap() =
132            snap.jn_document_from_root_document.clone();
133        *self.jn_frame_from_document_frames.write().unwrap() =
134            snap.jn_frame_from_document_frames.clone();
135        *self.jn_list_from_document_lists.write().unwrap() =
136            snap.jn_list_from_document_lists.clone();
137        *self.jn_resource_from_document_resources.write().unwrap() =
138            snap.jn_resource_from_document_resources.clone();
139        *self.jn_table_from_document_tables.write().unwrap() =
140            snap.jn_table_from_document_tables.clone();
141        *self.jn_block_from_frame_blocks.write().unwrap() = snap.jn_block_from_frame_blocks.clone();
142        *self.jn_frame_from_frame_parent_frame.write().unwrap() =
143            snap.jn_frame_from_frame_parent_frame.clone();
144        *self.jn_table_from_frame_table.write().unwrap() = snap.jn_table_from_frame_table.clone();
145        *self.jn_inline_element_from_block_elements.write().unwrap() =
146            snap.jn_inline_element_from_block_elements.clone();
147        *self.jn_list_from_block_list.write().unwrap() = snap.jn_list_from_block_list.clone();
148        *self.jn_table_cell_from_table_cells.write().unwrap() =
149            snap.jn_table_cell_from_table_cells.clone();
150        *self.jn_frame_from_table_cell_cell_frame.write().unwrap() =
151            snap.jn_frame_from_table_cell_cell_frame.clone();
152        *self.counters.write().unwrap() = snap.counters.clone();
153    }
154
155    /// Create a savepoint by snapshotting the entire store. Returns a savepoint id.
156    /// O(1) thanks to im::HashMap structural sharing.
157    pub fn create_savepoint(&self) -> u64 {
158        let snap = self.snapshot();
159        let mut id_counter = self.next_savepoint_id.write().unwrap();
160        let id = *id_counter;
161        *id_counter += 1;
162        self.savepoints.write().unwrap().insert(id, snap);
163        id
164    }
165
166    /// Restore the store to a previously created savepoint.
167    pub fn restore_savepoint(&self, savepoint_id: u64) {
168        let snap = self
169            .savepoints
170            .read()
171            .unwrap()
172            .get(&savepoint_id)
173            .expect("savepoint not found")
174            .clone();
175        self.restore(&snap);
176    }
177
178    /// Discard a savepoint without restoring (used on successful commit).
179    pub fn discard_savepoint(&self, savepoint_id: u64) {
180        self.savepoints.write().unwrap().remove(&savepoint_id);
181    }
182
183    /// Get-and-increment counter for an entity type.
184    pub(crate) fn next_id(&self, entity_name: &str) -> EntityId {
185        let mut counters = self.counters.write().unwrap();
186        let counter = counters.entry(entity_name.to_string()).or_insert(1);
187        let id = *counter;
188        *counter += 1;
189        id
190    }
191
192    /// Restore entity and junction data but preserve current counters.
193    /// Used for undo snapshots where IDs must remain monotonically increasing.
194    pub fn restore_without_counters(&self, snap: &HashMapStoreSnapshot) {
195        *self.roots.write().unwrap() = snap.roots.clone();
196        *self.documents.write().unwrap() = snap.documents.clone();
197        *self.frames.write().unwrap() = snap.frames.clone();
198        *self.blocks.write().unwrap() = snap.blocks.clone();
199        *self.inline_elements.write().unwrap() = snap.inline_elements.clone();
200        *self.lists.write().unwrap() = snap.lists.clone();
201        *self.resources.write().unwrap() = snap.resources.clone();
202        *self.tables.write().unwrap() = snap.tables.clone();
203        *self.table_cells.write().unwrap() = snap.table_cells.clone();
204        *self.jn_document_from_root_document.write().unwrap() =
205            snap.jn_document_from_root_document.clone();
206        *self.jn_frame_from_document_frames.write().unwrap() =
207            snap.jn_frame_from_document_frames.clone();
208        *self.jn_list_from_document_lists.write().unwrap() =
209            snap.jn_list_from_document_lists.clone();
210        *self.jn_resource_from_document_resources.write().unwrap() =
211            snap.jn_resource_from_document_resources.clone();
212        *self.jn_table_from_document_tables.write().unwrap() =
213            snap.jn_table_from_document_tables.clone();
214        *self.jn_block_from_frame_blocks.write().unwrap() = snap.jn_block_from_frame_blocks.clone();
215        *self.jn_frame_from_frame_parent_frame.write().unwrap() =
216            snap.jn_frame_from_frame_parent_frame.clone();
217        *self.jn_table_from_frame_table.write().unwrap() = snap.jn_table_from_frame_table.clone();
218        *self.jn_inline_element_from_block_elements.write().unwrap() =
219            snap.jn_inline_element_from_block_elements.clone();
220        *self.jn_list_from_block_list.write().unwrap() = snap.jn_list_from_block_list.clone();
221        *self.jn_table_cell_from_table_cells.write().unwrap() =
222            snap.jn_table_cell_from_table_cells.clone();
223        *self.jn_frame_from_table_cell_cell_frame.write().unwrap() =
224            snap.jn_frame_from_table_cell_cell_frame.clone();
225        // counters intentionally NOT restored — IDs must remain monotonically increasing
226    }
227
228    /// Create a type-erased store snapshot for undo.
229    pub fn store_snapshot(&self) -> StoreSnapshot {
230        StoreSnapshot::new(self.snapshot())
231    }
232
233    /// Restore from a type-erased store snapshot (preserves counters).
234    pub fn restore_store_snapshot(&self, snap: &StoreSnapshot) {
235        let s = snap
236            .downcast_ref::<HashMapStoreSnapshot>()
237            .expect("StoreSnapshot must contain HashMapStoreSnapshot");
238        self.restore_without_counters(s);
239    }
240}
241
242/// Snapshot of the entire store. O(1) to create thanks to im::HashMap structural sharing.
243#[derive(Debug, Clone)]
244pub struct HashMapStoreSnapshot {
245    roots: HashMap<EntityId, Root>,
246    documents: HashMap<EntityId, Document>,
247    frames: HashMap<EntityId, Frame>,
248    blocks: HashMap<EntityId, Block>,
249    inline_elements: HashMap<EntityId, InlineElement>,
250    lists: HashMap<EntityId, List>,
251    resources: HashMap<EntityId, Resource>,
252    tables: HashMap<EntityId, Table>,
253    table_cells: HashMap<EntityId, TableCell>,
254    jn_document_from_root_document: HashMap<EntityId, Vec<EntityId>>,
255    jn_frame_from_document_frames: HashMap<EntityId, Vec<EntityId>>,
256    jn_list_from_document_lists: HashMap<EntityId, Vec<EntityId>>,
257    jn_resource_from_document_resources: HashMap<EntityId, Vec<EntityId>>,
258    jn_table_from_document_tables: HashMap<EntityId, Vec<EntityId>>,
259    jn_block_from_frame_blocks: HashMap<EntityId, Vec<EntityId>>,
260    jn_frame_from_frame_parent_frame: HashMap<EntityId, Vec<EntityId>>,
261    jn_table_from_frame_table: HashMap<EntityId, Vec<EntityId>>,
262    jn_inline_element_from_block_elements: HashMap<EntityId, Vec<EntityId>>,
263    jn_list_from_block_list: HashMap<EntityId, Vec<EntityId>>,
264    jn_table_cell_from_table_cells: HashMap<EntityId, Vec<EntityId>>,
265    jn_frame_from_table_cell_cell_frame: HashMap<EntityId, Vec<EntityId>>,
266    counters: std::collections::HashMap<String, EntityId>,
267}
268
269impl StoreSnapshotTrait for HashMapStoreSnapshot {
270    fn clone_box(&self) -> Box<dyn StoreSnapshotTrait> {
271        Box::new(self.clone())
272    }
273
274    fn as_any(&self) -> &dyn std::any::Any {
275        self
276    }
277}
278
279// ─────────────────────────────────────────────────────────────────────────────
280// Helper functions
281// ─────────────────────────────────────────────────────────────────────────────
282
283pub(crate) fn delete_from_backward_junction(
284    junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
285    id: &EntityId,
286) {
287    let mut jn = junction.write().unwrap();
288    let keys: Vec<EntityId> = jn.keys().copied().collect();
289    for k in keys {
290        if let Some(right_ids) = jn.get(&k)
291            && right_ids.contains(id)
292        {
293            let filtered: Vec<EntityId> =
294                right_ids.iter().copied().filter(|eid| eid != id).collect();
295            jn.insert(k, filtered);
296        }
297    }
298}
299
300pub(crate) fn junction_get(
301    junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
302    id: &EntityId,
303) -> Vec<EntityId> {
304    junction
305        .read()
306        .unwrap()
307        .get(id)
308        .cloned()
309        .unwrap_or_default()
310}
311
312pub(crate) fn junction_set(
313    junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
314    id: EntityId,
315    ids: Vec<EntityId>,
316) {
317    junction.write().unwrap().insert(id, ids);
318}
319
320pub(crate) fn junction_remove(junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>, id: &EntityId) {
321    junction.write().unwrap().remove(id);
322}
323
324pub(crate) fn junction_get_relationships_from_right_ids(
325    junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
326    right_ids: &[EntityId],
327) -> Vec<(EntityId, Vec<EntityId>)> {
328    let jn = junction.read().unwrap();
329    jn.iter()
330        .filter(|(_, rids)| right_ids.iter().any(|eid| rids.contains(eid)))
331        .map(|(left_id, rids)| (*left_id, rids.clone()))
332        .collect()
333}
334
335pub(crate) fn junction_move_ids(
336    junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
337    id: &EntityId,
338    ids_to_move: &[EntityId],
339    new_index: i32,
340) -> Vec<EntityId> {
341    let current = junction_get(junction, id);
342    if ids_to_move.is_empty() {
343        return current;
344    }
345    let move_set: std::collections::HashSet<EntityId> = ids_to_move.iter().copied().collect();
346    let mut remaining: Vec<EntityId> = current
347        .into_iter()
348        .filter(|eid| !move_set.contains(eid))
349        .collect();
350    let insert_pos = if new_index < 0 || (new_index as usize) > remaining.len() {
351        remaining.len()
352    } else {
353        new_index as usize
354    };
355    for (i, &eid) in ids_to_move.iter().enumerate() {
356        remaining.insert(insert_pos + i, eid);
357    }
358    junction_set(junction, *id, remaining.clone());
359    remaining
360}
361
362// ═════════════════════════════════════════════════════════════════════════════
363// Macros for entities WITH relationships (reduces boilerplate)
364// ═════════════════════════════════════════════════════════════════════════════
365
366/// Generate standard relationship methods for a HashMap table implementation.
367#[macro_export]
368macro_rules! impl_relationship_methods {
369    ($table_type:ty, $field_enum:ty) => {
370        fn get_relationship(
371            &self,
372            id: &$crate::types::EntityId,
373            field: &$field_enum,
374        ) -> Result<Vec<$crate::types::EntityId>, $crate::error::RepositoryError> {
375            Ok($crate::database::hashmap_store::junction_get(
376                self.resolve_junction(field),
377                id,
378            ))
379        }
380
381        fn get_relationship_many(
382            &self,
383            ids: &[$crate::types::EntityId],
384            field: &$field_enum,
385        ) -> Result<
386            std::collections::HashMap<$crate::types::EntityId, Vec<$crate::types::EntityId>>,
387            $crate::error::RepositoryError,
388        > {
389            let jn = self.resolve_junction(field);
390            let mut map = std::collections::HashMap::new();
391            for id in ids {
392                map.insert(*id, $crate::database::hashmap_store::junction_get(jn, id));
393            }
394            Ok(map)
395        }
396
397        fn get_relationship_count(
398            &self,
399            id: &$crate::types::EntityId,
400            field: &$field_enum,
401        ) -> Result<usize, $crate::error::RepositoryError> {
402            Ok(
403                $crate::database::hashmap_store::junction_get(self.resolve_junction(field), id)
404                    .len(),
405            )
406        }
407
408        fn get_relationship_in_range(
409            &self,
410            id: &$crate::types::EntityId,
411            field: &$field_enum,
412            offset: usize,
413            limit: usize,
414        ) -> Result<Vec<$crate::types::EntityId>, $crate::error::RepositoryError> {
415            let all =
416                $crate::database::hashmap_store::junction_get(self.resolve_junction(field), id);
417            Ok(all.into_iter().skip(offset).take(limit).collect())
418        }
419
420        fn get_relationships_from_right_ids(
421            &self,
422            field: &$field_enum,
423            right_ids: &[$crate::types::EntityId],
424        ) -> Result<
425            Vec<($crate::types::EntityId, Vec<$crate::types::EntityId>)>,
426            $crate::error::RepositoryError,
427        > {
428            Ok(
429                $crate::database::hashmap_store::junction_get_relationships_from_right_ids(
430                    self.resolve_junction(field),
431                    right_ids,
432                ),
433            )
434        }
435    };
436}
437
438#[macro_export]
439macro_rules! impl_write_relationship_methods {
440    ($table_type:ty, $field_enum:ty) => {
441        $crate::impl_relationship_methods!($table_type, $field_enum);
442
443        fn set_relationship_multi(
444            &mut self,
445            field: &$field_enum,
446            relationships: Vec<($crate::types::EntityId, Vec<$crate::types::EntityId>)>,
447        ) -> Result<(), $crate::error::RepositoryError> {
448            let jn = self.resolve_junction(field);
449            for (left_id, entities) in relationships {
450                $crate::database::hashmap_store::junction_set(jn, left_id, entities);
451            }
452            Ok(())
453        }
454
455        fn set_relationship(
456            &mut self,
457            id: &$crate::types::EntityId,
458            field: &$field_enum,
459            right_ids: &[$crate::types::EntityId],
460        ) -> Result<(), $crate::error::RepositoryError> {
461            $crate::database::hashmap_store::junction_set(
462                self.resolve_junction(field),
463                *id,
464                right_ids.to_vec(),
465            );
466            Ok(())
467        }
468
469        fn move_relationship_ids(
470            &mut self,
471            id: &$crate::types::EntityId,
472            field: &$field_enum,
473            ids_to_move: &[$crate::types::EntityId],
474            new_index: i32,
475        ) -> Result<Vec<$crate::types::EntityId>, $crate::error::RepositoryError> {
476            Ok($crate::database::hashmap_store::junction_move_ids(
477                self.resolve_junction(field),
478                id,
479                ids_to_move,
480                new_index,
481            ))
482        }
483    };
484}
485
486// ═════════════════════════════════════════════════════════════════════════════
487// Leaf entity macro (entities without forward relationships)
488// ═════════════════════════════════════════════════════════════════════════════
489
490#[macro_export]
491macro_rules! impl_leaf_entity_table {
492    (
493        entity: $Entity:ident,
494        entity_name: $entity_name:expr,
495        store_field: $store_field:ident,
496        table_trait: $TableTrait:ident,
497        table_ro_trait: $TableROTrait:ident,
498        table_struct: $TableStruct:ident,
499        table_ro_struct: $TableROStruct:ident,
500        backward_junctions: [ $( ($bj_field:ident) ),* $(,)? ],
501    ) => {
502        pub struct $TableStruct<'a> {
503            store: &'a $crate::database::hashmap_store::HashMapStore,
504        }
505
506        impl<'a> $TableStruct<'a> {
507            pub fn new(store: &'a $crate::database::hashmap_store::HashMapStore) -> Self {
508                Self { store }
509            }
510        }
511
512        impl<'a> $TableTrait for $TableStruct<'a> {
513            fn create(&mut self, entity: &$Entity) -> Result<$Entity, $crate::error::RepositoryError> {
514                self.create_multi(std::slice::from_ref(entity))
515                    .map(|v| v.into_iter().next().unwrap())
516            }
517
518            fn create_multi(&mut self, entities: &[$Entity]) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
519                let mut created = Vec::with_capacity(entities.len());
520                let mut map = self.store.$store_field.write().unwrap();
521
522                for entity in entities {
523                    let new_entity = if entity.id == $crate::types::EntityId::default() {
524                        let id = self.store.next_id($entity_name);
525                        $Entity {
526                            id,
527                            ..entity.clone()
528                        }
529                    } else {
530                        if map.contains_key(&entity.id) {
531                            return Err($crate::error::RepositoryError::DuplicateId {
532                                entity: stringify!($Entity),
533                                id: entity.id,
534                            });
535                        }
536                        entity.clone()
537                    };
538
539                    map.insert(new_entity.id, new_entity.clone());
540                    created.push(new_entity);
541                }
542                Ok(created)
543            }
544
545            fn get(&self, id: &$crate::types::EntityId) -> Result<Option<$Entity>, $crate::error::RepositoryError> {
546                Ok(self.store.$store_field.read().unwrap().get(id).cloned())
547            }
548
549            fn get_multi(&self, ids: &[$crate::types::EntityId]) -> Result<Vec<Option<$Entity>>, $crate::error::RepositoryError> {
550                let map = self.store.$store_field.read().unwrap();
551                Ok(ids.iter().map(|id| map.get(id).cloned()).collect())
552            }
553
554            fn get_all(&self) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
555                Ok(self.store.$store_field.read().unwrap().values().cloned().collect())
556            }
557
558            fn update(&mut self, entity: &$Entity) -> Result<$Entity, $crate::error::RepositoryError> {
559                self.update_multi(std::slice::from_ref(entity))
560                    .map(|v| v.into_iter().next().unwrap())
561            }
562
563            fn update_multi(&mut self, entities: &[$Entity]) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
564                let mut map = self.store.$store_field.write().unwrap();
565                let mut result = Vec::with_capacity(entities.len());
566                for entity in entities {
567                    map.insert(entity.id, entity.clone());
568                    result.push(entity.clone());
569                }
570                Ok(result)
571            }
572
573            fn update_with_relationships(&mut self, entity: &$Entity) -> Result<$Entity, $crate::error::RepositoryError> {
574                self.update(entity)
575            }
576
577            fn update_with_relationships_multi(&mut self, entities: &[$Entity]) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
578                self.update_multi(entities)
579            }
580
581            fn remove(&mut self, id: &$crate::types::EntityId) -> Result<(), $crate::error::RepositoryError> {
582                self.remove_multi(std::slice::from_ref(id))
583            }
584
585            fn remove_multi(&mut self, ids: &[$crate::types::EntityId]) -> Result<(), $crate::error::RepositoryError> {
586                let mut map = self.store.$store_field.write().unwrap();
587                for id in ids {
588                    map.remove(id);
589                    $(
590                        $crate::database::hashmap_store::delete_from_backward_junction(&self.store.$bj_field, id);
591                    )*
592                }
593                Ok(())
594            }
595        }
596
597        pub struct $TableROStruct<'a> {
598            store: &'a $crate::database::hashmap_store::HashMapStore,
599        }
600
601        impl<'a> $TableROStruct<'a> {
602            pub fn new(store: &'a $crate::database::hashmap_store::HashMapStore) -> Self {
603                Self { store }
604            }
605        }
606
607        impl<'a> $TableROTrait for $TableROStruct<'a> {
608            fn get(&self, id: &$crate::types::EntityId) -> Result<Option<$Entity>, $crate::error::RepositoryError> {
609                Ok(self.store.$store_field.read().unwrap().get(id).cloned())
610            }
611
612            fn get_multi(&self, ids: &[$crate::types::EntityId]) -> Result<Vec<Option<$Entity>>, $crate::error::RepositoryError> {
613                let map = self.store.$store_field.read().unwrap();
614                Ok(ids.iter().map(|id| map.get(id).cloned()).collect())
615            }
616
617            fn get_all(&self) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
618                Ok(self.store.$store_field.read().unwrap().values().cloned().collect())
619            }
620        }
621    };
622}