Skip to main content

text_document_common/database/
hashmap_store.rs

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