Skip to main content

common/database/
hashmap_store.rs

1// Generated by Qleany v1.7.0 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 workspaces: RwLock<HashMap<EntityId, Workspace>>,
25    pub systems: RwLock<HashMap<EntityId, System>>,
26    pub entitys: RwLock<HashMap<EntityId, Entity>>,
27    pub fields: RwLock<HashMap<EntityId, Field>>,
28    pub features: RwLock<HashMap<EntityId, Feature>>,
29    pub files: RwLock<HashMap<EntityId, File>>,
30    pub use_cases: RwLock<HashMap<EntityId, UseCase>>,
31    pub dtos: RwLock<HashMap<EntityId, Dto>>,
32    pub dto_fields: RwLock<HashMap<EntityId, DtoField>>,
33    pub globals: RwLock<HashMap<EntityId, Global>>,
34    pub relationships: RwLock<HashMap<EntityId, Relationship>>,
35    pub user_interfaces: RwLock<HashMap<EntityId, UserInterface>>,
36
37    // ── Junction tables (one per forward relationship, shared for backward cleanup) ─
38    pub jn_system_from_root_system: RwLock<HashMap<EntityId, Vec<EntityId>>>,
39    pub jn_workspace_from_root_workspace: RwLock<HashMap<EntityId, Vec<EntityId>>>,
40    pub jn_entity_from_workspace_entities: RwLock<HashMap<EntityId, Vec<EntityId>>>,
41    pub jn_feature_from_workspace_features: RwLock<HashMap<EntityId, Vec<EntityId>>>,
42    pub jn_global_from_workspace_global: RwLock<HashMap<EntityId, Vec<EntityId>>>,
43    pub jn_user_interface_from_workspace_user_interface: RwLock<HashMap<EntityId, Vec<EntityId>>>,
44    pub jn_file_from_system_files: RwLock<HashMap<EntityId, Vec<EntityId>>>,
45    pub jn_field_from_entity_fields: RwLock<HashMap<EntityId, Vec<EntityId>>>,
46    pub jn_entity_from_entity_inherits_from: RwLock<HashMap<EntityId, Vec<EntityId>>>,
47    pub jn_relationship_from_entity_relationships: RwLock<HashMap<EntityId, Vec<EntityId>>>,
48    pub jn_entity_from_field_entity: RwLock<HashMap<EntityId, Vec<EntityId>>>,
49    pub jn_use_case_from_feature_use_cases: RwLock<HashMap<EntityId, Vec<EntityId>>>,
50    pub jn_entity_from_file_entity: RwLock<HashMap<EntityId, Vec<EntityId>>>,
51    pub jn_feature_from_file_feature: RwLock<HashMap<EntityId, Vec<EntityId>>>,
52    pub jn_field_from_file_field: RwLock<HashMap<EntityId, Vec<EntityId>>>,
53    pub jn_use_case_from_file_use_case: RwLock<HashMap<EntityId, Vec<EntityId>>>,
54    pub jn_dto_from_use_case_dto_in: RwLock<HashMap<EntityId, Vec<EntityId>>>,
55    pub jn_dto_from_use_case_dto_out: RwLock<HashMap<EntityId, Vec<EntityId>>>,
56    pub jn_entity_from_use_case_entities: RwLock<HashMap<EntityId, Vec<EntityId>>>,
57    pub jn_dto_field_from_dto_fields: RwLock<HashMap<EntityId, Vec<EntityId>>>,
58    pub jn_entity_from_relationship_left_entity: RwLock<HashMap<EntityId, Vec<EntityId>>>,
59    pub jn_entity_from_relationship_right_entity: RwLock<HashMap<EntityId, Vec<EntityId>>>,
60
61    // ── ID counters (one per entity type) ──────────────────────────
62    pub counters: RwLock<std::collections::HashMap<String, EntityId>>,
63
64    // ── Savepoints ──────────────────────────────────────────────────
65    savepoints: RwLock<std::collections::HashMap<u64, HashMapStoreSnapshot>>,
66    next_savepoint_id: RwLock<u64>,
67}
68
69impl HashMapStore {
70    pub fn new() -> Self {
71        Self::default()
72    }
73
74    /// Clone the entire store for savepoint support. O(1) thanks to im::HashMap.
75    pub fn snapshot(&self) -> HashMapStoreSnapshot {
76        HashMapStoreSnapshot {
77            roots: self.roots.read().unwrap().clone(),
78            workspaces: self.workspaces.read().unwrap().clone(),
79            systems: self.systems.read().unwrap().clone(),
80            entitys: self.entitys.read().unwrap().clone(),
81            fields: self.fields.read().unwrap().clone(),
82            features: self.features.read().unwrap().clone(),
83            files: self.files.read().unwrap().clone(),
84            use_cases: self.use_cases.read().unwrap().clone(),
85            dtos: self.dtos.read().unwrap().clone(),
86            dto_fields: self.dto_fields.read().unwrap().clone(),
87            globals: self.globals.read().unwrap().clone(),
88            relationships: self.relationships.read().unwrap().clone(),
89            user_interfaces: self.user_interfaces.read().unwrap().clone(),
90            jn_system_from_root_system: self.jn_system_from_root_system.read().unwrap().clone(),
91            jn_workspace_from_root_workspace: self
92                .jn_workspace_from_root_workspace
93                .read()
94                .unwrap()
95                .clone(),
96            jn_entity_from_workspace_entities: self
97                .jn_entity_from_workspace_entities
98                .read()
99                .unwrap()
100                .clone(),
101            jn_feature_from_workspace_features: self
102                .jn_feature_from_workspace_features
103                .read()
104                .unwrap()
105                .clone(),
106            jn_global_from_workspace_global: self
107                .jn_global_from_workspace_global
108                .read()
109                .unwrap()
110                .clone(),
111            jn_user_interface_from_workspace_user_interface: self
112                .jn_user_interface_from_workspace_user_interface
113                .read()
114                .unwrap()
115                .clone(),
116            jn_file_from_system_files: self.jn_file_from_system_files.read().unwrap().clone(),
117            jn_field_from_entity_fields: self.jn_field_from_entity_fields.read().unwrap().clone(),
118            jn_entity_from_entity_inherits_from: self
119                .jn_entity_from_entity_inherits_from
120                .read()
121                .unwrap()
122                .clone(),
123            jn_relationship_from_entity_relationships: self
124                .jn_relationship_from_entity_relationships
125                .read()
126                .unwrap()
127                .clone(),
128            jn_entity_from_field_entity: self.jn_entity_from_field_entity.read().unwrap().clone(),
129            jn_use_case_from_feature_use_cases: self
130                .jn_use_case_from_feature_use_cases
131                .read()
132                .unwrap()
133                .clone(),
134            jn_entity_from_file_entity: self.jn_entity_from_file_entity.read().unwrap().clone(),
135            jn_feature_from_file_feature: self.jn_feature_from_file_feature.read().unwrap().clone(),
136            jn_field_from_file_field: self.jn_field_from_file_field.read().unwrap().clone(),
137            jn_use_case_from_file_use_case: self
138                .jn_use_case_from_file_use_case
139                .read()
140                .unwrap()
141                .clone(),
142            jn_dto_from_use_case_dto_in: self.jn_dto_from_use_case_dto_in.read().unwrap().clone(),
143            jn_dto_from_use_case_dto_out: self.jn_dto_from_use_case_dto_out.read().unwrap().clone(),
144            jn_entity_from_use_case_entities: self
145                .jn_entity_from_use_case_entities
146                .read()
147                .unwrap()
148                .clone(),
149            jn_dto_field_from_dto_fields: self.jn_dto_field_from_dto_fields.read().unwrap().clone(),
150            jn_entity_from_relationship_left_entity: self
151                .jn_entity_from_relationship_left_entity
152                .read()
153                .unwrap()
154                .clone(),
155            jn_entity_from_relationship_right_entity: self
156                .jn_entity_from_relationship_right_entity
157                .read()
158                .unwrap()
159                .clone(),
160            counters: self.counters.read().unwrap().clone(),
161        }
162    }
163
164    /// Restore from a savepoint snapshot.
165    pub fn restore(&self, snap: &HashMapStoreSnapshot) {
166        *self.roots.write().unwrap() = snap.roots.clone();
167        *self.workspaces.write().unwrap() = snap.workspaces.clone();
168        *self.systems.write().unwrap() = snap.systems.clone();
169        *self.entitys.write().unwrap() = snap.entitys.clone();
170        *self.fields.write().unwrap() = snap.fields.clone();
171        *self.features.write().unwrap() = snap.features.clone();
172        *self.files.write().unwrap() = snap.files.clone();
173        *self.use_cases.write().unwrap() = snap.use_cases.clone();
174        *self.dtos.write().unwrap() = snap.dtos.clone();
175        *self.dto_fields.write().unwrap() = snap.dto_fields.clone();
176        *self.globals.write().unwrap() = snap.globals.clone();
177        *self.relationships.write().unwrap() = snap.relationships.clone();
178        *self.user_interfaces.write().unwrap() = snap.user_interfaces.clone();
179        *self.jn_system_from_root_system.write().unwrap() = snap.jn_system_from_root_system.clone();
180        *self.jn_workspace_from_root_workspace.write().unwrap() =
181            snap.jn_workspace_from_root_workspace.clone();
182        *self.jn_entity_from_workspace_entities.write().unwrap() =
183            snap.jn_entity_from_workspace_entities.clone();
184        *self.jn_feature_from_workspace_features.write().unwrap() =
185            snap.jn_feature_from_workspace_features.clone();
186        *self.jn_global_from_workspace_global.write().unwrap() =
187            snap.jn_global_from_workspace_global.clone();
188        *self
189            .jn_user_interface_from_workspace_user_interface
190            .write()
191            .unwrap() = snap.jn_user_interface_from_workspace_user_interface.clone();
192        *self.jn_file_from_system_files.write().unwrap() = snap.jn_file_from_system_files.clone();
193        *self.jn_field_from_entity_fields.write().unwrap() =
194            snap.jn_field_from_entity_fields.clone();
195        *self.jn_entity_from_entity_inherits_from.write().unwrap() =
196            snap.jn_entity_from_entity_inherits_from.clone();
197        *self
198            .jn_relationship_from_entity_relationships
199            .write()
200            .unwrap() = snap.jn_relationship_from_entity_relationships.clone();
201        *self.jn_entity_from_field_entity.write().unwrap() =
202            snap.jn_entity_from_field_entity.clone();
203        *self.jn_use_case_from_feature_use_cases.write().unwrap() =
204            snap.jn_use_case_from_feature_use_cases.clone();
205        *self.jn_entity_from_file_entity.write().unwrap() = snap.jn_entity_from_file_entity.clone();
206        *self.jn_feature_from_file_feature.write().unwrap() =
207            snap.jn_feature_from_file_feature.clone();
208        *self.jn_field_from_file_field.write().unwrap() = snap.jn_field_from_file_field.clone();
209        *self.jn_use_case_from_file_use_case.write().unwrap() =
210            snap.jn_use_case_from_file_use_case.clone();
211        *self.jn_dto_from_use_case_dto_in.write().unwrap() =
212            snap.jn_dto_from_use_case_dto_in.clone();
213        *self.jn_dto_from_use_case_dto_out.write().unwrap() =
214            snap.jn_dto_from_use_case_dto_out.clone();
215        *self.jn_entity_from_use_case_entities.write().unwrap() =
216            snap.jn_entity_from_use_case_entities.clone();
217        *self.jn_dto_field_from_dto_fields.write().unwrap() =
218            snap.jn_dto_field_from_dto_fields.clone();
219        *self
220            .jn_entity_from_relationship_left_entity
221            .write()
222            .unwrap() = snap.jn_entity_from_relationship_left_entity.clone();
223        *self
224            .jn_entity_from_relationship_right_entity
225            .write()
226            .unwrap() = snap.jn_entity_from_relationship_right_entity.clone();
227        *self.counters.write().unwrap() = snap.counters.clone();
228    }
229
230    /// Create a savepoint by snapshotting the entire store. Returns a savepoint id.
231    /// O(1) thanks to im::HashMap structural sharing.
232    pub fn create_savepoint(&self) -> u64 {
233        let snap = self.snapshot();
234        let mut id_counter = self.next_savepoint_id.write().unwrap();
235        let id = *id_counter;
236        *id_counter += 1;
237        self.savepoints.write().unwrap().insert(id, snap);
238        id
239    }
240
241    /// Restore the store to a previously created savepoint.
242    pub fn restore_savepoint(&self, savepoint_id: u64) {
243        let snap = self
244            .savepoints
245            .read()
246            .unwrap()
247            .get(&savepoint_id)
248            .expect("savepoint not found")
249            .clone();
250        self.restore(&snap);
251    }
252
253    /// Discard a savepoint without restoring (used on successful commit).
254    pub fn discard_savepoint(&self, savepoint_id: u64) {
255        self.savepoints.write().unwrap().remove(&savepoint_id);
256    }
257
258    /// Get-and-increment counter for an entity type.
259    pub(crate) fn next_id(&self, entity_name: &str) -> EntityId {
260        let mut counters = self.counters.write().unwrap();
261        let counter = counters.entry(entity_name.to_string()).or_insert(1);
262        let id = *counter;
263        *counter += 1;
264        id
265    }
266
267    /// Restore entity and junction data but preserve current counters.
268    /// Used for undo snapshots where IDs must remain monotonically increasing.
269    pub fn restore_without_counters(&self, snap: &HashMapStoreSnapshot) {
270        *self.roots.write().unwrap() = snap.roots.clone();
271        *self.workspaces.write().unwrap() = snap.workspaces.clone();
272        *self.systems.write().unwrap() = snap.systems.clone();
273        *self.entitys.write().unwrap() = snap.entitys.clone();
274        *self.fields.write().unwrap() = snap.fields.clone();
275        *self.features.write().unwrap() = snap.features.clone();
276        *self.files.write().unwrap() = snap.files.clone();
277        *self.use_cases.write().unwrap() = snap.use_cases.clone();
278        *self.dtos.write().unwrap() = snap.dtos.clone();
279        *self.dto_fields.write().unwrap() = snap.dto_fields.clone();
280        *self.globals.write().unwrap() = snap.globals.clone();
281        *self.relationships.write().unwrap() = snap.relationships.clone();
282        *self.user_interfaces.write().unwrap() = snap.user_interfaces.clone();
283        *self.jn_system_from_root_system.write().unwrap() = snap.jn_system_from_root_system.clone();
284        *self.jn_workspace_from_root_workspace.write().unwrap() =
285            snap.jn_workspace_from_root_workspace.clone();
286        *self.jn_entity_from_workspace_entities.write().unwrap() =
287            snap.jn_entity_from_workspace_entities.clone();
288        *self.jn_feature_from_workspace_features.write().unwrap() =
289            snap.jn_feature_from_workspace_features.clone();
290        *self.jn_global_from_workspace_global.write().unwrap() =
291            snap.jn_global_from_workspace_global.clone();
292        *self
293            .jn_user_interface_from_workspace_user_interface
294            .write()
295            .unwrap() = snap.jn_user_interface_from_workspace_user_interface.clone();
296        *self.jn_file_from_system_files.write().unwrap() = snap.jn_file_from_system_files.clone();
297        *self.jn_field_from_entity_fields.write().unwrap() =
298            snap.jn_field_from_entity_fields.clone();
299        *self.jn_entity_from_entity_inherits_from.write().unwrap() =
300            snap.jn_entity_from_entity_inherits_from.clone();
301        *self
302            .jn_relationship_from_entity_relationships
303            .write()
304            .unwrap() = snap.jn_relationship_from_entity_relationships.clone();
305        *self.jn_entity_from_field_entity.write().unwrap() =
306            snap.jn_entity_from_field_entity.clone();
307        *self.jn_use_case_from_feature_use_cases.write().unwrap() =
308            snap.jn_use_case_from_feature_use_cases.clone();
309        *self.jn_entity_from_file_entity.write().unwrap() = snap.jn_entity_from_file_entity.clone();
310        *self.jn_feature_from_file_feature.write().unwrap() =
311            snap.jn_feature_from_file_feature.clone();
312        *self.jn_field_from_file_field.write().unwrap() = snap.jn_field_from_file_field.clone();
313        *self.jn_use_case_from_file_use_case.write().unwrap() =
314            snap.jn_use_case_from_file_use_case.clone();
315        *self.jn_dto_from_use_case_dto_in.write().unwrap() =
316            snap.jn_dto_from_use_case_dto_in.clone();
317        *self.jn_dto_from_use_case_dto_out.write().unwrap() =
318            snap.jn_dto_from_use_case_dto_out.clone();
319        *self.jn_entity_from_use_case_entities.write().unwrap() =
320            snap.jn_entity_from_use_case_entities.clone();
321        *self.jn_dto_field_from_dto_fields.write().unwrap() =
322            snap.jn_dto_field_from_dto_fields.clone();
323        *self
324            .jn_entity_from_relationship_left_entity
325            .write()
326            .unwrap() = snap.jn_entity_from_relationship_left_entity.clone();
327        *self
328            .jn_entity_from_relationship_right_entity
329            .write()
330            .unwrap() = snap.jn_entity_from_relationship_right_entity.clone();
331        // counters intentionally NOT restored — IDs must remain monotonically increasing
332    }
333
334    /// Create a type-erased store snapshot for undo.
335    pub fn store_snapshot(&self) -> StoreSnapshot {
336        StoreSnapshot::new(self.snapshot())
337    }
338
339    /// Restore from a type-erased store snapshot (preserves counters).
340    pub fn restore_store_snapshot(&self, snap: &StoreSnapshot) {
341        let s = snap
342            .downcast_ref::<HashMapStoreSnapshot>()
343            .expect("StoreSnapshot must contain HashMapStoreSnapshot");
344        self.restore_without_counters(s);
345    }
346}
347
348/// Snapshot of the entire store. O(1) to create thanks to im::HashMap structural sharing.
349#[derive(Debug, Clone)]
350pub struct HashMapStoreSnapshot {
351    roots: HashMap<EntityId, Root>,
352    workspaces: HashMap<EntityId, Workspace>,
353    systems: HashMap<EntityId, System>,
354    entitys: HashMap<EntityId, Entity>,
355    fields: HashMap<EntityId, Field>,
356    features: HashMap<EntityId, Feature>,
357    files: HashMap<EntityId, File>,
358    use_cases: HashMap<EntityId, UseCase>,
359    dtos: HashMap<EntityId, Dto>,
360    dto_fields: HashMap<EntityId, DtoField>,
361    globals: HashMap<EntityId, Global>,
362    relationships: HashMap<EntityId, Relationship>,
363    user_interfaces: HashMap<EntityId, UserInterface>,
364    jn_system_from_root_system: HashMap<EntityId, Vec<EntityId>>,
365    jn_workspace_from_root_workspace: HashMap<EntityId, Vec<EntityId>>,
366    jn_entity_from_workspace_entities: HashMap<EntityId, Vec<EntityId>>,
367    jn_feature_from_workspace_features: HashMap<EntityId, Vec<EntityId>>,
368    jn_global_from_workspace_global: HashMap<EntityId, Vec<EntityId>>,
369    jn_user_interface_from_workspace_user_interface: HashMap<EntityId, Vec<EntityId>>,
370    jn_file_from_system_files: HashMap<EntityId, Vec<EntityId>>,
371    jn_field_from_entity_fields: HashMap<EntityId, Vec<EntityId>>,
372    jn_entity_from_entity_inherits_from: HashMap<EntityId, Vec<EntityId>>,
373    jn_relationship_from_entity_relationships: HashMap<EntityId, Vec<EntityId>>,
374    jn_entity_from_field_entity: HashMap<EntityId, Vec<EntityId>>,
375    jn_use_case_from_feature_use_cases: HashMap<EntityId, Vec<EntityId>>,
376    jn_entity_from_file_entity: HashMap<EntityId, Vec<EntityId>>,
377    jn_feature_from_file_feature: HashMap<EntityId, Vec<EntityId>>,
378    jn_field_from_file_field: HashMap<EntityId, Vec<EntityId>>,
379    jn_use_case_from_file_use_case: HashMap<EntityId, Vec<EntityId>>,
380    jn_dto_from_use_case_dto_in: HashMap<EntityId, Vec<EntityId>>,
381    jn_dto_from_use_case_dto_out: HashMap<EntityId, Vec<EntityId>>,
382    jn_entity_from_use_case_entities: HashMap<EntityId, Vec<EntityId>>,
383    jn_dto_field_from_dto_fields: HashMap<EntityId, Vec<EntityId>>,
384    jn_entity_from_relationship_left_entity: HashMap<EntityId, Vec<EntityId>>,
385    jn_entity_from_relationship_right_entity: HashMap<EntityId, Vec<EntityId>>,
386    counters: std::collections::HashMap<String, EntityId>,
387}
388
389impl StoreSnapshotTrait for HashMapStoreSnapshot {
390    fn clone_box(&self) -> Box<dyn StoreSnapshotTrait> {
391        Box::new(self.clone())
392    }
393
394    fn as_any(&self) -> &dyn std::any::Any {
395        self
396    }
397}
398
399// ─────────────────────────────────────────────────────────────────────────────
400// Helper functions
401// ─────────────────────────────────────────────────────────────────────────────
402
403pub(crate) fn delete_from_backward_junction(
404    junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
405    id: &EntityId,
406) {
407    let mut jn = junction.write().unwrap();
408    let keys: Vec<EntityId> = jn.keys().copied().collect();
409    for k in keys {
410        if let Some(right_ids) = jn.get(&k)
411            && right_ids.contains(id)
412        {
413            let filtered: Vec<EntityId> =
414                right_ids.iter().copied().filter(|eid| eid != id).collect();
415            jn.insert(k, filtered);
416        }
417    }
418}
419
420pub(crate) fn junction_get(
421    junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
422    id: &EntityId,
423) -> Vec<EntityId> {
424    junction
425        .read()
426        .unwrap()
427        .get(id)
428        .cloned()
429        .unwrap_or_default()
430}
431
432pub(crate) fn junction_set(
433    junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
434    id: EntityId,
435    ids: Vec<EntityId>,
436) {
437    junction.write().unwrap().insert(id, ids);
438}
439
440pub(crate) fn junction_remove(junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>, id: &EntityId) {
441    junction.write().unwrap().remove(id);
442}
443
444pub(crate) fn junction_get_relationships_from_right_ids(
445    junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
446    right_ids: &[EntityId],
447) -> Vec<(EntityId, Vec<EntityId>)> {
448    let jn = junction.read().unwrap();
449    jn.iter()
450        .filter(|(_, rids)| right_ids.iter().any(|eid| rids.contains(eid)))
451        .map(|(left_id, rids)| (*left_id, rids.clone()))
452        .collect()
453}
454
455pub(crate) fn junction_move_ids(
456    junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
457    id: &EntityId,
458    ids_to_move: &[EntityId],
459    new_index: i32,
460) -> Vec<EntityId> {
461    let current = junction_get(junction, id);
462    if ids_to_move.is_empty() {
463        return current;
464    }
465    let move_set: std::collections::HashSet<EntityId> = ids_to_move.iter().copied().collect();
466    let mut remaining: Vec<EntityId> = current
467        .into_iter()
468        .filter(|eid| !move_set.contains(eid))
469        .collect();
470    let insert_pos = if new_index < 0 || (new_index as usize) > remaining.len() {
471        remaining.len()
472    } else {
473        new_index as usize
474    };
475    for (i, &eid) in ids_to_move.iter().enumerate() {
476        remaining.insert(insert_pos + i, eid);
477    }
478    junction_set(junction, *id, remaining.clone());
479    remaining
480}
481
482// ═════════════════════════════════════════════════════════════════════════════
483// Macros for entities WITH relationships (reduces boilerplate)
484// ═════════════════════════════════════════════════════════════════════════════
485
486/// Generate standard relationship methods for a HashMap table implementation.
487#[macro_export]
488macro_rules! impl_relationship_methods {
489    ($table_type:ty, $field_enum:ty) => {
490        fn get_relationship(
491            &self,
492            id: &$crate::types::EntityId,
493            field: &$field_enum,
494        ) -> Result<Vec<$crate::types::EntityId>, $crate::error::RepositoryError> {
495            Ok($crate::database::hashmap_store::junction_get(
496                self.resolve_junction(field),
497                id,
498            ))
499        }
500
501        fn get_relationship_many(
502            &self,
503            ids: &[$crate::types::EntityId],
504            field: &$field_enum,
505        ) -> Result<
506            std::collections::HashMap<$crate::types::EntityId, Vec<$crate::types::EntityId>>,
507            $crate::error::RepositoryError,
508        > {
509            let jn = self.resolve_junction(field);
510            let mut map = std::collections::HashMap::new();
511            for id in ids {
512                map.insert(*id, $crate::database::hashmap_store::junction_get(jn, id));
513            }
514            Ok(map)
515        }
516
517        fn get_relationship_count(
518            &self,
519            id: &$crate::types::EntityId,
520            field: &$field_enum,
521        ) -> Result<usize, $crate::error::RepositoryError> {
522            Ok(
523                $crate::database::hashmap_store::junction_get(self.resolve_junction(field), id)
524                    .len(),
525            )
526        }
527
528        fn get_relationship_in_range(
529            &self,
530            id: &$crate::types::EntityId,
531            field: &$field_enum,
532            offset: usize,
533            limit: usize,
534        ) -> Result<Vec<$crate::types::EntityId>, $crate::error::RepositoryError> {
535            let all =
536                $crate::database::hashmap_store::junction_get(self.resolve_junction(field), id);
537            Ok(all.into_iter().skip(offset).take(limit).collect())
538        }
539
540        fn get_relationships_from_right_ids(
541            &self,
542            field: &$field_enum,
543            right_ids: &[$crate::types::EntityId],
544        ) -> Result<
545            Vec<($crate::types::EntityId, Vec<$crate::types::EntityId>)>,
546            $crate::error::RepositoryError,
547        > {
548            Ok(
549                $crate::database::hashmap_store::junction_get_relationships_from_right_ids(
550                    self.resolve_junction(field),
551                    right_ids,
552                ),
553            )
554        }
555    };
556}
557
558#[macro_export]
559macro_rules! impl_write_relationship_methods {
560    ($table_type:ty, $field_enum:ty) => {
561        $crate::impl_relationship_methods!($table_type, $field_enum);
562
563        fn set_relationship_multi(
564            &mut self,
565            field: &$field_enum,
566            relationships: Vec<($crate::types::EntityId, Vec<$crate::types::EntityId>)>,
567        ) -> Result<(), $crate::error::RepositoryError> {
568            let jn = self.resolve_junction(field);
569            for (left_id, entities) in relationships {
570                $crate::database::hashmap_store::junction_set(jn, left_id, entities);
571            }
572            Ok(())
573        }
574
575        fn set_relationship(
576            &mut self,
577            id: &$crate::types::EntityId,
578            field: &$field_enum,
579            right_ids: &[$crate::types::EntityId],
580        ) -> Result<(), $crate::error::RepositoryError> {
581            $crate::database::hashmap_store::junction_set(
582                self.resolve_junction(field),
583                *id,
584                right_ids.to_vec(),
585            );
586            Ok(())
587        }
588
589        fn move_relationship_ids(
590            &mut self,
591            id: &$crate::types::EntityId,
592            field: &$field_enum,
593            ids_to_move: &[$crate::types::EntityId],
594            new_index: i32,
595        ) -> Result<Vec<$crate::types::EntityId>, $crate::error::RepositoryError> {
596            Ok($crate::database::hashmap_store::junction_move_ids(
597                self.resolve_junction(field),
598                id,
599                ids_to_move,
600                new_index,
601            ))
602        }
603    };
604}
605
606// ═════════════════════════════════════════════════════════════════════════════
607// Leaf entity macro (entities without forward relationships)
608// ═════════════════════════════════════════════════════════════════════════════
609
610#[macro_export]
611macro_rules! impl_leaf_entity_table {
612    (
613        entity: $Entity:ident,
614        entity_name: $entity_name:expr,
615        store_field: $store_field:ident,
616        table_trait: $TableTrait:ident,
617        table_ro_trait: $TableROTrait:ident,
618        table_struct: $TableStruct:ident,
619        table_ro_struct: $TableROStruct:ident,
620        backward_junctions: [ $( ($bj_field:ident) ),* $(,)? ],
621    ) => {
622        pub struct $TableStruct<'a> {
623            store: &'a $crate::database::hashmap_store::HashMapStore,
624        }
625
626        impl<'a> $TableStruct<'a> {
627            pub fn new(store: &'a $crate::database::hashmap_store::HashMapStore) -> Self {
628                Self { store }
629            }
630        }
631
632        impl<'a> $TableTrait for $TableStruct<'a> {
633            fn create(&mut self, entity: &$Entity) -> Result<$Entity, $crate::error::RepositoryError> {
634                self.create_multi(std::slice::from_ref(entity))
635                    .map(|v| v.into_iter().next().unwrap())
636            }
637
638            fn create_multi(&mut self, entities: &[$Entity]) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
639                let mut created = Vec::with_capacity(entities.len());
640                let mut map = self.store.$store_field.write().unwrap();
641
642                for entity in entities {
643                    let new_entity = if entity.id == $crate::types::EntityId::default() {
644                        let id = self.store.next_id($entity_name);
645                        $Entity {
646                            id,
647                            ..entity.clone()
648                        }
649                    } else {
650                        if map.contains_key(&entity.id) {
651                            return Err($crate::error::RepositoryError::DuplicateId {
652                                entity: stringify!($Entity),
653                                id: entity.id,
654                            });
655                        }
656                        entity.clone()
657                    };
658
659                    map.insert(new_entity.id, new_entity.clone());
660                    created.push(new_entity);
661                }
662                Ok(created)
663            }
664
665            fn get(&self, id: &$crate::types::EntityId) -> Result<Option<$Entity>, $crate::error::RepositoryError> {
666                Ok(self.store.$store_field.read().unwrap().get(id).cloned())
667            }
668
669            fn get_multi(&self, ids: &[$crate::types::EntityId]) -> Result<Vec<Option<$Entity>>, $crate::error::RepositoryError> {
670                let map = self.store.$store_field.read().unwrap();
671                Ok(ids.iter().map(|id| map.get(id).cloned()).collect())
672            }
673
674            fn get_all(&self) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
675                Ok(self.store.$store_field.read().unwrap().values().cloned().collect())
676            }
677
678            fn update(&mut self, entity: &$Entity) -> Result<$Entity, $crate::error::RepositoryError> {
679                self.update_multi(std::slice::from_ref(entity))
680                    .map(|v| v.into_iter().next().unwrap())
681            }
682
683            fn update_multi(&mut self, entities: &[$Entity]) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
684                let mut map = self.store.$store_field.write().unwrap();
685                let mut result = Vec::with_capacity(entities.len());
686                for entity in entities {
687                    map.insert(entity.id, entity.clone());
688                    result.push(entity.clone());
689                }
690                Ok(result)
691            }
692
693            fn update_with_relationships(&mut self, entity: &$Entity) -> Result<$Entity, $crate::error::RepositoryError> {
694                self.update(entity)
695            }
696
697            fn update_with_relationships_multi(&mut self, entities: &[$Entity]) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
698                self.update_multi(entities)
699            }
700
701            fn remove(&mut self, id: &$crate::types::EntityId) -> Result<(), $crate::error::RepositoryError> {
702                self.remove_multi(std::slice::from_ref(id))
703            }
704
705            fn remove_multi(&mut self, ids: &[$crate::types::EntityId]) -> Result<(), $crate::error::RepositoryError> {
706                let mut map = self.store.$store_field.write().unwrap();
707                for id in ids {
708                    map.remove(id);
709                    $(
710                        $crate::database::hashmap_store::delete_from_backward_junction(&self.store.$bj_field, id);
711                    )*
712                }
713                Ok(())
714            }
715        }
716
717        pub struct $TableROStruct<'a> {
718            store: &'a $crate::database::hashmap_store::HashMapStore,
719        }
720
721        impl<'a> $TableROStruct<'a> {
722            pub fn new(store: &'a $crate::database::hashmap_store::HashMapStore) -> Self {
723                Self { store }
724            }
725        }
726
727        impl<'a> $TableROTrait for $TableROStruct<'a> {
728            fn get(&self, id: &$crate::types::EntityId) -> Result<Option<$Entity>, $crate::error::RepositoryError> {
729                Ok(self.store.$store_field.read().unwrap().get(id).cloned())
730            }
731
732            fn get_multi(&self, ids: &[$crate::types::EntityId]) -> Result<Vec<Option<$Entity>>, $crate::error::RepositoryError> {
733                let map = self.store.$store_field.read().unwrap();
734                Ok(ids.iter().map(|id| map.get(id).cloned()).collect())
735            }
736
737            fn get_all(&self) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
738                Ok(self.store.$store_field.read().unwrap().values().cloned().collect())
739            }
740        }
741    };
742}