Skip to main content

common/direct_access/system/
system_repository.rs

1// Generated by Qleany v1.11.0 from common_entity_repository.tera
2
3use std::fmt::Display;
4
5use crate::{
6    database::hashmap_store::HashMapStoreSnapshot,
7    // `*_or_recover` rather than a plain `.read()/.write().unwrap()`: a panic
8    // anywhere under a table's guard poisons that lock for the rest of the
9    // process, and the release profile unwinds, so the poison is reachable in a
10    // shipped build. Recovering (and clearing) it keeps one unrelated failure
11    // from escalating into a second panic on every later access to the store.
12    database::hashmap_store::{read_or_recover, write_or_recover},
13    database::transactions::Transaction,
14    direct_access::repository_factory,
15    entities::System,
16    event::{DirectAccessEntity, EntityEvent, Event, EventBuffer, Origin},
17    snapshot::EntityTreeSnapshot,
18    types::EntityId,
19};
20
21use crate::direct_access::root::RootRelationshipField;
22use crate::error::RepositoryError;
23use serde::{Deserialize, Serialize};
24
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26pub enum SystemRelationshipField {
27    Files,
28}
29
30impl Display for SystemRelationshipField {
31    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
32        write!(f, "{:?}", self)
33    }
34}
35
36pub trait SystemTable {
37    fn create(&mut self, entity: &System) -> Result<System, RepositoryError>;
38    fn create_multi(&mut self, entities: &[System]) -> Result<Vec<System>, RepositoryError>;
39    fn get(&self, id: &EntityId) -> Result<Option<System>, RepositoryError>;
40    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<System>>, RepositoryError>;
41    fn get_all(&self) -> Result<Vec<System>, RepositoryError>;
42    fn update(&mut self, entity: &System) -> Result<System, RepositoryError>;
43    fn update_multi(&mut self, entities: &[System]) -> Result<Vec<System>, RepositoryError>;
44    fn update_with_relationships(&mut self, entity: &System) -> Result<System, RepositoryError>;
45    fn update_with_relationships_multi(
46        &mut self,
47        entities: &[System],
48    ) -> Result<Vec<System>, RepositoryError>;
49    fn remove(&mut self, id: &EntityId) -> Result<(), RepositoryError>;
50    fn remove_multi(&mut self, ids: &[EntityId]) -> Result<(), RepositoryError>;
51    fn get_relationship(
52        &self,
53        id: &EntityId,
54        field: &SystemRelationshipField,
55    ) -> Result<Vec<EntityId>, RepositoryError>;
56    fn get_relationship_many(
57        &self,
58        ids: &[EntityId],
59        field: &SystemRelationshipField,
60    ) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError>;
61    fn get_relationship_count(
62        &self,
63        id: &EntityId,
64        field: &SystemRelationshipField,
65    ) -> Result<usize, RepositoryError>;
66    fn get_relationship_in_range(
67        &self,
68        id: &EntityId,
69        field: &SystemRelationshipField,
70        offset: usize,
71        limit: usize,
72    ) -> Result<Vec<EntityId>, RepositoryError>;
73    fn get_relationships_from_right_ids(
74        &self,
75        field: &SystemRelationshipField,
76        right_ids: &[EntityId],
77    ) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError>;
78    fn set_relationship_multi(
79        &mut self,
80        field: &SystemRelationshipField,
81        relationships: Vec<(EntityId, Vec<EntityId>)>,
82    ) -> Result<(), RepositoryError>;
83    fn set_relationship(
84        &mut self,
85        id: &EntityId,
86        field: &SystemRelationshipField,
87        right_ids: &[EntityId],
88    ) -> Result<(), RepositoryError>;
89    fn move_relationship_ids(
90        &mut self,
91        id: &EntityId,
92        field: &SystemRelationshipField,
93        ids_to_move: &[EntityId],
94        new_index: i32,
95    ) -> Result<Vec<EntityId>, RepositoryError>;
96}
97
98pub trait SystemTableRO {
99    fn get(&self, id: &EntityId) -> Result<Option<System>, RepositoryError>;
100    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<System>>, RepositoryError>;
101    fn get_all(&self) -> Result<Vec<System>, RepositoryError>;
102    fn get_relationship(
103        &self,
104        id: &EntityId,
105        field: &SystemRelationshipField,
106    ) -> Result<Vec<EntityId>, RepositoryError>;
107    fn get_relationship_many(
108        &self,
109        ids: &[EntityId],
110        field: &SystemRelationshipField,
111    ) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError>;
112    fn get_relationship_count(
113        &self,
114        id: &EntityId,
115        field: &SystemRelationshipField,
116    ) -> Result<usize, RepositoryError>;
117    fn get_relationship_in_range(
118        &self,
119        id: &EntityId,
120        field: &SystemRelationshipField,
121        offset: usize,
122        limit: usize,
123    ) -> Result<Vec<EntityId>, RepositoryError>;
124    fn get_relationships_from_right_ids(
125        &self,
126        field: &SystemRelationshipField,
127        right_ids: &[EntityId],
128    ) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError>;
129}
130
131pub struct SystemRepository<'a> {
132    table: Box<dyn SystemTable + 'a>,
133    transaction: &'a Transaction,
134}
135
136impl<'a> SystemRepository<'a> {
137    pub fn new(table: Box<dyn SystemTable + 'a>, transaction: &'a Transaction) -> Self {
138        SystemRepository { table, transaction }
139    }
140
141    pub fn create_orphan(
142        &mut self,
143        event_buffer: &mut EventBuffer,
144        entity: &System,
145    ) -> Result<System, RepositoryError> {
146        let new = self.table.create(entity)?;
147        event_buffer.push(Event {
148            origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Created)),
149            ids: vec![new.id],
150            data: None,
151        });
152        Ok(new)
153    }
154
155    pub fn create_orphan_multi(
156        &mut self,
157        event_buffer: &mut EventBuffer,
158        entities: &[System],
159    ) -> Result<Vec<System>, RepositoryError> {
160        let new_entities = self.table.create_multi(entities)?;
161        event_buffer.push(Event {
162            origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Created)),
163            ids: new_entities.iter().map(|e| e.id).collect(),
164            data: None,
165        });
166        Ok(new_entities)
167    }
168    pub fn create(
169        &mut self,
170        event_buffer: &mut EventBuffer,
171        entity: &System,
172        owner_id: EntityId,
173        _index: i32,
174    ) -> Result<System, RepositoryError> {
175        let new = self.table.create(entity)?;
176        let created_id = new.id;
177
178        let mut relationship_ids = self.get_relationships_from_owner(&owner_id)?;
179        if relationship_ids.is_empty() {
180            relationship_ids = vec![created_id];
181        } else {
182            // Replace existing relationship: cascade-remove old entities first
183            self.remove_multi(event_buffer, &relationship_ids)?;
184            relationship_ids = vec![created_id];
185        }
186
187        self.set_relationships_in_owner(event_buffer, &owner_id, &relationship_ids)?;
188        event_buffer.push(Event {
189            origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Created)),
190            ids: vec![created_id],
191            data: None,
192        });
193        Ok(new)
194    }
195
196    pub fn create_multi(
197        &mut self,
198        event_buffer: &mut EventBuffer,
199        entities: &[System],
200        owner_id: EntityId,
201        _index: i32,
202    ) -> Result<Vec<System>, RepositoryError> {
203        let new_entities = self.table.create_multi(entities)?;
204        let created_ids: Vec<EntityId> = new_entities.iter().map(|e| e.id).collect();
205
206        let mut relationship_ids = self.get_relationships_from_owner(&owner_id)?;
207        if relationship_ids.is_empty() {
208            relationship_ids = created_ids.clone();
209        } else {
210            self.remove_multi(event_buffer, &relationship_ids)?;
211            relationship_ids = created_ids.clone();
212        }
213
214        self.set_relationships_in_owner(event_buffer, &owner_id, &relationship_ids)?;
215        event_buffer.push(Event {
216            origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Created)),
217            ids: created_ids,
218            data: None,
219        });
220        Ok(new_entities)
221    }
222
223    pub fn get(&self, id: &EntityId) -> Result<Option<System>, RepositoryError> {
224        self.table.get(id)
225    }
226    pub fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<System>>, RepositoryError> {
227        self.table.get_multi(ids)
228    }
229    pub fn get_all(&self) -> Result<Vec<System>, RepositoryError> {
230        self.table.get_all()
231    }
232
233    pub fn update(
234        &mut self,
235        event_buffer: &mut EventBuffer,
236        entity: &System,
237    ) -> Result<System, RepositoryError> {
238        let updated = self.table.update(entity)?;
239        event_buffer.push(Event {
240            origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Updated)),
241            ids: vec![updated.id],
242            data: None,
243        });
244        Ok(updated)
245    }
246
247    pub fn update_multi(
248        &mut self,
249        event_buffer: &mut EventBuffer,
250        entities: &[System],
251    ) -> Result<Vec<System>, RepositoryError> {
252        let updated = self.table.update_multi(entities)?;
253        event_buffer.push(Event {
254            origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Updated)),
255            ids: updated.iter().map(|e| e.id).collect(),
256            data: None,
257        });
258        Ok(updated)
259    }
260
261    pub fn update_with_relationships(
262        &mut self,
263        event_buffer: &mut EventBuffer,
264        entity: &System,
265    ) -> Result<System, RepositoryError> {
266        let updated = self.table.update_with_relationships(entity)?;
267        event_buffer.push(Event {
268            origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Updated)),
269            ids: vec![updated.id],
270            data: None,
271        });
272        Ok(updated)
273    }
274
275    pub fn update_with_relationships_multi(
276        &mut self,
277        event_buffer: &mut EventBuffer,
278        entities: &[System],
279    ) -> Result<Vec<System>, RepositoryError> {
280        let updated = self.table.update_with_relationships_multi(entities)?;
281        event_buffer.push(Event {
282            origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Updated)),
283            ids: updated.iter().map(|e| e.id).collect(),
284            data: None,
285        });
286        Ok(updated)
287    }
288
289    pub fn remove(
290        &mut self,
291        event_buffer: &mut EventBuffer,
292        id: &EntityId,
293    ) -> Result<(), RepositoryError> {
294        let entity = match self.table.get(id)? {
295            Some(e) => e,
296            None => return Ok(()),
297        };
298        // get all strong forward relationship fields
299
300        let files = entity.files.clone();
301
302        // remove all strong relationships, initiating a cascade remove
303
304        repository_factory::write::create_file_repository(self.transaction)?
305            .remove_multi(event_buffer, &files)?;
306        // Before removal, find which owner(s) reference this entity
307        let affected_owner_ids: Vec<EntityId> = {
308            let owner_repo = repository_factory::write::create_root_repository(self.transaction)?;
309            owner_repo
310                .get_relationships_from_right_ids(&RootRelationshipField::System, &[*id])?
311                .into_iter()
312                .map(|(owner_id, _)| owner_id)
313                .collect()
314        };
315        // Save each owner's current relationship IDs (properly ordered via get_relationships_from_owner)
316        let mut owner_rel_before: std::collections::HashMap<EntityId, Vec<EntityId>> =
317            std::collections::HashMap::new();
318        for owner_id in &affected_owner_ids {
319            owner_rel_before.insert(*owner_id, self.get_relationships_from_owner(owner_id)?);
320        }
321
322        // remove entity
323        self.table.remove(id)?;
324        event_buffer.push(Event {
325            origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Removed)),
326            ids: vec![*id],
327            data: None,
328        });
329        // Update each affected owner's relationship to exclude removed ID (emits Updated event)
330        for owner_id in &affected_owner_ids {
331            if let Some(rel_ids) = owner_rel_before.get(owner_id) {
332                let updated: Vec<EntityId> =
333                    rel_ids.iter().copied().filter(|rid| *rid != *id).collect();
334                self.set_relationships_in_owner(event_buffer, owner_id, &updated)?;
335            }
336        }
337
338        Ok(())
339    }
340
341    pub fn remove_multi(
342        &mut self,
343        event_buffer: &mut EventBuffer,
344        ids: &[EntityId],
345    ) -> Result<(), RepositoryError> {
346        let entities = self.table.get_multi(ids)?;
347        if entities.is_empty() || entities.iter().all(|e| e.is_none()) {
348            return Ok(());
349        }
350
351        // get all strong forward relationship fields
352
353        let mut files_ids: Vec<EntityId> = entities
354            .iter()
355            .flat_map(|entity| entity.as_ref().map(|entity| entity.files.clone()))
356            .flatten()
357            .collect();
358        // remove duplicates
359        files_ids.sort();
360        files_ids.dedup();
361
362        // remove all strong relationships, initiating a cascade remove
363
364        repository_factory::write::create_file_repository(self.transaction)?
365            .remove_multi(event_buffer, &files_ids)?;
366        // Before removal, find which owner(s) reference these entities
367        let affected_owner_ids: Vec<EntityId> = {
368            let owner_repo = repository_factory::write::create_root_repository(self.transaction)?;
369            owner_repo
370                .get_relationships_from_right_ids(&RootRelationshipField::System, ids)?
371                .into_iter()
372                .map(|(owner_id, _)| owner_id)
373                .collect()
374        };
375        // Save each owner's current relationship IDs (properly ordered via get_relationships_from_owner)
376        let mut owner_rel_before: std::collections::HashMap<EntityId, Vec<EntityId>> =
377            std::collections::HashMap::new();
378        for owner_id in &affected_owner_ids {
379            owner_rel_before.insert(*owner_id, self.get_relationships_from_owner(owner_id)?);
380        }
381
382        self.table.remove_multi(ids)?;
383        event_buffer.push(Event {
384            origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Removed)),
385            ids: ids.into(),
386            data: None,
387        });
388        // Update each affected owner's relationship to exclude removed IDs (emits Updated event)
389        {
390            let removed_set: std::collections::HashSet<EntityId> = ids.iter().copied().collect();
391            for owner_id in &affected_owner_ids {
392                if let Some(rel_ids) = owner_rel_before.get(owner_id) {
393                    let updated: Vec<EntityId> = rel_ids
394                        .iter()
395                        .copied()
396                        .filter(|rid| !removed_set.contains(rid))
397                        .collect();
398                    self.set_relationships_in_owner(event_buffer, owner_id, &updated)?;
399                }
400            }
401        }
402
403        Ok(())
404    }
405    pub fn get_relationship(
406        &self,
407        id: &EntityId,
408        field: &SystemRelationshipField,
409    ) -> Result<Vec<EntityId>, RepositoryError> {
410        self.table.get_relationship(id, field)
411    }
412    pub fn get_relationship_many(
413        &self,
414        ids: &[EntityId],
415        field: &SystemRelationshipField,
416    ) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError> {
417        self.table.get_relationship_many(ids, field)
418    }
419    pub fn get_relationship_count(
420        &self,
421        id: &EntityId,
422        field: &SystemRelationshipField,
423    ) -> Result<usize, RepositoryError> {
424        self.table.get_relationship_count(id, field)
425    }
426    pub fn get_relationship_in_range(
427        &self,
428        id: &EntityId,
429        field: &SystemRelationshipField,
430        offset: usize,
431        limit: usize,
432    ) -> Result<Vec<EntityId>, RepositoryError> {
433        self.table
434            .get_relationship_in_range(id, field, offset, limit)
435    }
436    pub fn get_relationships_from_right_ids(
437        &self,
438        field: &SystemRelationshipField,
439        right_ids: &[EntityId],
440    ) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError> {
441        self.table
442            .get_relationships_from_right_ids(field, right_ids)
443    }
444
445    pub fn set_relationship_multi(
446        &mut self,
447        event_buffer: &mut EventBuffer,
448        field: &SystemRelationshipField,
449        relationships: Vec<(EntityId, Vec<EntityId>)>,
450    ) -> Result<(), RepositoryError> {
451        // Validate that all right_ids exist
452        let all_right_ids: Vec<EntityId> = relationships
453            .iter()
454            .flat_map(|(_, ids)| ids.iter().copied())
455            .collect();
456        if !all_right_ids.is_empty() {
457            match field {
458                SystemRelationshipField::Files => {
459                    let child_repo =
460                        repository_factory::write::create_file_repository(self.transaction)?;
461                    let found = child_repo.get_multi(&all_right_ids)?;
462                    let missing: Vec<_> = all_right_ids
463                        .iter()
464                        .zip(found.iter())
465                        .filter(|(_, entity)| entity.is_none())
466                        .map(|(id, _)| *id)
467                        .collect();
468                    if !missing.is_empty() {
469                        return Err(RepositoryError::MissingRelationshipTarget {
470                            operation: "set_relationship_multi",
471                            ids: missing,
472                        });
473                    }
474                }
475            }
476        }
477        self.table
478            .set_relationship_multi(field, relationships.clone())?;
479        for (left_id, right_ids) in relationships {
480            event_buffer.push(Event {
481                origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Updated)),
482                ids: vec![left_id],
483                data: Some(format!(
484                    "{}:{}",
485                    field,
486                    right_ids
487                        .iter()
488                        .map(|id| id.to_string())
489                        .collect::<Vec<_>>()
490                        .join(",")
491                )),
492            });
493        }
494        Ok(())
495    }
496
497    pub fn set_relationship(
498        &mut self,
499        event_buffer: &mut EventBuffer,
500        id: &EntityId,
501        field: &SystemRelationshipField,
502        right_ids: &[EntityId],
503    ) -> Result<(), RepositoryError> {
504        // Validate that all right_ids exist
505        if !right_ids.is_empty() {
506            match field {
507                SystemRelationshipField::Files => {
508                    let child_repo =
509                        repository_factory::write::create_file_repository(self.transaction)?;
510                    let found = child_repo.get_multi(right_ids)?;
511                    let missing: Vec<_> = right_ids
512                        .iter()
513                        .zip(found.iter())
514                        .filter(|(_, entity)| entity.is_none())
515                        .map(|(id, _)| *id)
516                        .collect();
517                    if !missing.is_empty() {
518                        return Err(RepositoryError::MissingRelationshipTarget {
519                            operation: "set_relationship",
520                            ids: missing,
521                        });
522                    }
523                }
524            }
525        }
526        self.table.set_relationship(id, field, right_ids)?;
527        event_buffer.push(Event {
528            origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Updated)),
529            ids: vec![*id],
530            data: Some(format!(
531                "{}:{}",
532                field,
533                right_ids
534                    .iter()
535                    .map(|id| id.to_string())
536                    .collect::<Vec<_>>()
537                    .join(",")
538            )),
539        });
540        Ok(())
541    }
542
543    pub fn move_relationship_ids(
544        &mut self,
545        event_buffer: &mut EventBuffer,
546        id: &EntityId,
547        field: &SystemRelationshipField,
548        ids_to_move: &[EntityId],
549        new_index: i32,
550    ) -> Result<Vec<EntityId>, RepositoryError> {
551        let reordered = self
552            .table
553            .move_relationship_ids(id, field, ids_to_move, new_index)?;
554        event_buffer.push(Event {
555            origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Updated)),
556            ids: vec![*id],
557            data: Some(format!(
558                "{}:{}",
559                field,
560                reordered
561                    .iter()
562                    .map(|id| id.to_string())
563                    .collect::<Vec<_>>()
564                    .join(",")
565            )),
566        });
567        Ok(reordered)
568    }
569    pub fn get_relationships_from_owner(
570        &self,
571        owner_id: &EntityId,
572    ) -> Result<Vec<EntityId>, RepositoryError> {
573        let repo = repository_factory::write::create_root_repository(self.transaction)?;
574        repo.get_relationship(owner_id, &RootRelationshipField::System)
575    }
576
577    pub fn set_relationships_in_owner(
578        &mut self,
579        event_buffer: &mut EventBuffer,
580        owner_id: &EntityId,
581        ids: &[EntityId],
582    ) -> Result<(), RepositoryError> {
583        let mut repo = repository_factory::write::create_root_repository(self.transaction)?;
584        repo.set_relationship(event_buffer, owner_id, &RootRelationshipField::System, ids)
585    }
586
587    pub fn snapshot(&self, ids: &[EntityId]) -> Result<EntityTreeSnapshot, RepositoryError> {
588        // O(1) whole-store capture (im::HashMap structural sharing). Restore is scoped to `ids`.
589        let store_snap = self.transaction.snapshot_store();
590        Ok(EntityTreeSnapshot {
591            store_snapshot: Some(store_snap),
592            root_ids: ids.to_vec(),
593        })
594    }
595
596    pub fn restore(
597        &mut self,
598        event_buffer: &mut EventBuffer,
599        snap: &EntityTreeSnapshot,
600    ) -> Result<(), RepositoryError> {
601        let store_snap = snap
602            .store_snapshot
603            .as_ref()
604            .ok_or_else(|| RepositoryError::Serialization("missing store snapshot".into()))?;
605        let snap_store = store_snap
606            .downcast_ref::<HashMapStoreSnapshot>()
607            .ok_or_else(|| RepositoryError::Serialization("snapshot type mismatch".into()))?;
608
609        // Scoped restore: walk the subtree rooted at the captured ids (strong relationships only),
610        // in both the snapshot and the live store, and reconcile only it.
611        let mut visited = std::collections::HashSet::new();
612        self.restore_subtree(event_buffer, snap_store, &snap.root_ids, &mut visited)?;
613
614        // Surgically reconcile the root ids' placement in their (external) strong owner,
615        // preserving sibling edits made on other undo stacks.
616        self.reconcile_backref_root_system(event_buffer, snap_store, &snap.root_ids)?;
617
618        Ok(())
619    }
620
621    /// Restore the subtree rooted at `ids` (of this entity type) to the snapshot state, recursing
622    /// into strong children. Touches only the subtree: in-scope rows and the forward junctions they
623    /// own are restored wholesale; external referrers are reconciled surgically; everything else
624    /// (other trunks, non-undoable data) is left untouched. `visited` guards against cycles.
625    pub(crate) fn restore_subtree(
626        &self,
627        event_buffer: &mut EventBuffer,
628        snap: &HashMapStoreSnapshot,
629        ids: &[EntityId],
630        // Keyed by (entity type, id): EntityIds are per-type, not globally unique, so a bare
631        // id set would let one entity type's id falsely mask another's.
632        visited: &mut std::collections::HashSet<(&'static str, EntityId)>,
633    ) -> Result<(), RepositoryError> {
634        let ids: Vec<EntityId> = ids
635            .iter()
636            .copied()
637            .filter(|id| visited.insert(("system", *id)))
638            .collect();
639        if ids.is_empty() {
640            return Ok(());
641        }
642
643        let store = self.transaction.get_store();
644
645        // Three-way partition by presence in snapshot vs live store (read before mutating).
646        let mut to_create: Vec<EntityId> = Vec::new(); // in snapshot, gone from live -> re-add
647        let mut to_update: Vec<EntityId> = Vec::new(); // in both                    -> revert
648        let mut to_delete: Vec<EntityId> = Vec::new(); // live only (created after)  -> delete
649        {
650            let live = read_or_recover(&store.systems);
651            for id in &ids {
652                match (snap.systems.contains_key(id), live.contains_key(id)) {
653                    (true, false) => to_create.push(*id),
654                    (true, true) => to_update.push(*id),
655                    (false, true) => to_delete.push(*id),
656                    (false, false) => {}
657                }
658            }
659        }
660
661        // 1. Recurse into strong children first (collect child ids from snapshot + live, then drop
662        //    all locks before recursing so self-referential ownership cannot deadlock).
663        {
664            let mut child_ids: Vec<EntityId> = Vec::new();
665            for id in to_create.iter().chain(to_update.iter()) {
666                if let Some(list) = snap.jn_file_from_system_files.get(id) {
667                    child_ids.extend(list.iter().copied());
668                }
669            }
670            {
671                let live_jn = read_or_recover(&store.jn_file_from_system_files);
672                for id in &ids {
673                    if let Some(list) = live_jn.get(id) {
674                        child_ids.extend(list.iter().copied());
675                    }
676                }
677            }
678            child_ids.sort();
679            child_ids.dedup();
680            if !child_ids.is_empty() {
681                repository_factory::write::create_file_repository(self.transaction)?
682                    .restore_subtree(event_buffer, snap, &child_ids, visited)?;
683            }
684        }
685
686        // 2. Entity rows: revert/re-add from snapshot, delete the ones created after it.
687        {
688            let mut live = write_or_recover(&store.systems);
689            for id in to_create.iter().chain(to_update.iter()) {
690                if let Some(row) = snap.systems.get(id) {
691                    live.insert(*id, row.clone());
692                }
693            }
694            for id in &to_delete {
695                live.remove(id);
696            }
697        }
698
699        // 3. This entity's own forward junctions (strong + weak): restored wholesale because the
700        //    junction key is in-scope (owned exclusively by this trunk).
701        {
702            let mut live_jn = write_or_recover(&store.jn_file_from_system_files);
703            for id in to_create.iter().chain(to_update.iter()) {
704                match snap.jn_file_from_system_files.get(id) {
705                    Some(v) => {
706                        live_jn.insert(*id, v.clone());
707                    }
708                    None => {
709                        live_jn.remove(id);
710                    }
711                }
712            }
713            for id in &to_delete {
714                live_jn.remove(id);
715            }
716        }
717
718        // 4. External weak referrers of these ids: reconcile surgically (membership only).
719
720        // 5. Events: precise per-id (Created/Updated/Removed), not a whole-store storm.
721        if !to_create.is_empty() {
722            event_buffer.push(Event {
723                origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Created)),
724                ids: to_create.clone(),
725                data: None,
726            });
727            event_buffer.push(Event {
728                origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Updated)),
729                ids: to_create.clone(),
730                data: None,
731            });
732        }
733        if !to_update.is_empty() {
734            event_buffer.push(Event {
735                origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Updated)),
736                ids: to_update.clone(),
737                data: None,
738            });
739        }
740        if !to_delete.is_empty() {
741            event_buffer.push(Event {
742                origin: Origin::DirectAccess(DirectAccessEntity::System(EntityEvent::Removed)),
743                ids: to_delete.clone(),
744                data: None,
745            });
746        }
747
748        Ok(())
749    }
750
751    /// Surgically reconcile one external referrer junction (out-of-scope key -> in-scope values):
752    /// `root.system`.
753    fn reconcile_backref_root_system(
754        &self,
755        event_buffer: &mut EventBuffer,
756        snap: &HashMapStoreSnapshot,
757        scope_ids: &[EntityId],
758    ) -> Result<(), RepositoryError> {
759        let scope: std::collections::HashSet<EntityId> = scope_ids.iter().copied().collect();
760        if scope.is_empty() {
761            return Ok(());
762        }
763        let store = self.transaction.get_store();
764
765        // External left keys whose ordered list references any scope id, in snapshot or live.
766        let mut left_keys: std::collections::HashSet<EntityId> = std::collections::HashSet::new();
767        for (left, rights) in snap.jn_system_from_root_system.iter() {
768            if rights.iter().any(|rid| scope.contains(rid)) {
769                left_keys.insert(*left);
770            }
771        }
772        {
773            let live_jn = read_or_recover(&store.jn_system_from_root_system);
774            for (left, rights) in live_jn.iter() {
775                if rights.iter().any(|rid| scope.contains(rid)) {
776                    left_keys.insert(*left);
777                }
778            }
779        }
780
781        for left in left_keys {
782            let snap_list: Vec<EntityId> = snap
783                .jn_system_from_root_system
784                .get(&left)
785                .cloned()
786                .unwrap_or_default();
787            let new_list = {
788                let live_jn = read_or_recover(&store.jn_system_from_root_system);
789                let live_list: Vec<EntityId> = live_jn.get(&left).cloned().unwrap_or_default();
790                let reconciled = crate::database::hashmap_store::reconcile_backref_list(
791                    &live_list, &snap_list, &scope,
792                );
793                if reconciled == live_list {
794                    None
795                } else {
796                    Some(reconciled)
797                }
798            };
799            if let Some(reconciled) = new_list {
800                write_or_recover(&store.jn_system_from_root_system).insert(left, reconciled);
801                event_buffer.push(Event {
802                    origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Updated)),
803                    ids: vec![left],
804                    data: None,
805                });
806            }
807        }
808        Ok(())
809    }
810}
811
812pub struct SystemRepositoryRO<'a> {
813    table: Box<dyn SystemTableRO + 'a>,
814}
815impl<'a> SystemRepositoryRO<'a> {
816    pub fn new(table: Box<dyn SystemTableRO + 'a>) -> Self {
817        SystemRepositoryRO { table }
818    }
819    pub fn get(&self, id: &EntityId) -> Result<Option<System>, RepositoryError> {
820        self.table.get(id)
821    }
822    pub fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<System>>, RepositoryError> {
823        self.table.get_multi(ids)
824    }
825    pub fn get_all(&self) -> Result<Vec<System>, RepositoryError> {
826        self.table.get_all()
827    }
828    pub fn get_relationship(
829        &self,
830        id: &EntityId,
831        field: &SystemRelationshipField,
832    ) -> Result<Vec<EntityId>, RepositoryError> {
833        self.table.get_relationship(id, field)
834    }
835    pub fn get_relationship_many(
836        &self,
837        ids: &[EntityId],
838        field: &SystemRelationshipField,
839    ) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError> {
840        self.table.get_relationship_many(ids, field)
841    }
842    pub fn get_relationship_count(
843        &self,
844        id: &EntityId,
845        field: &SystemRelationshipField,
846    ) -> Result<usize, RepositoryError> {
847        self.table.get_relationship_count(id, field)
848    }
849    pub fn get_relationship_in_range(
850        &self,
851        id: &EntityId,
852        field: &SystemRelationshipField,
853        offset: usize,
854        limit: usize,
855    ) -> Result<Vec<EntityId>, RepositoryError> {
856        self.table
857            .get_relationship_in_range(id, field, offset, limit)
858    }
859    pub fn get_relationships_from_right_ids(
860        &self,
861        field: &SystemRelationshipField,
862        right_ids: &[EntityId],
863    ) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError> {
864        self.table
865            .get_relationships_from_right_ids(field, right_ids)
866    }
867}