Skip to main content

common/direct_access/use_case/
use_case_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::UseCase,
16    event::{DirectAccessEntity, EntityEvent, Event, EventBuffer, Origin},
17    snapshot::EntityTreeSnapshot,
18    types::EntityId,
19};
20
21use crate::direct_access::feature::FeatureRelationshipField;
22use crate::error::RepositoryError;
23use serde::{Deserialize, Serialize};
24
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26pub enum UseCaseRelationshipField {
27    DtoIn,
28    DtoOut,
29    Entities,
30}
31
32impl Display for UseCaseRelationshipField {
33    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
34        write!(f, "{:?}", self)
35    }
36}
37
38pub trait UseCaseTable {
39    fn create(&mut self, entity: &UseCase) -> Result<UseCase, RepositoryError>;
40    fn create_multi(&mut self, entities: &[UseCase]) -> Result<Vec<UseCase>, RepositoryError>;
41    fn get(&self, id: &EntityId) -> Result<Option<UseCase>, RepositoryError>;
42    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<UseCase>>, RepositoryError>;
43    fn get_all(&self) -> Result<Vec<UseCase>, RepositoryError>;
44    fn update(&mut self, entity: &UseCase) -> Result<UseCase, RepositoryError>;
45    fn update_multi(&mut self, entities: &[UseCase]) -> Result<Vec<UseCase>, RepositoryError>;
46    fn update_with_relationships(&mut self, entity: &UseCase) -> Result<UseCase, RepositoryError>;
47    fn update_with_relationships_multi(
48        &mut self,
49        entities: &[UseCase],
50    ) -> Result<Vec<UseCase>, RepositoryError>;
51    fn remove(&mut self, id: &EntityId) -> Result<(), RepositoryError>;
52    fn remove_multi(&mut self, ids: &[EntityId]) -> Result<(), RepositoryError>;
53    fn get_relationship(
54        &self,
55        id: &EntityId,
56        field: &UseCaseRelationshipField,
57    ) -> Result<Vec<EntityId>, RepositoryError>;
58    fn get_relationship_many(
59        &self,
60        ids: &[EntityId],
61        field: &UseCaseRelationshipField,
62    ) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError>;
63    fn get_relationship_count(
64        &self,
65        id: &EntityId,
66        field: &UseCaseRelationshipField,
67    ) -> Result<usize, RepositoryError>;
68    fn get_relationship_in_range(
69        &self,
70        id: &EntityId,
71        field: &UseCaseRelationshipField,
72        offset: usize,
73        limit: usize,
74    ) -> Result<Vec<EntityId>, RepositoryError>;
75    fn get_relationships_from_right_ids(
76        &self,
77        field: &UseCaseRelationshipField,
78        right_ids: &[EntityId],
79    ) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError>;
80    fn set_relationship_multi(
81        &mut self,
82        field: &UseCaseRelationshipField,
83        relationships: Vec<(EntityId, Vec<EntityId>)>,
84    ) -> Result<(), RepositoryError>;
85    fn set_relationship(
86        &mut self,
87        id: &EntityId,
88        field: &UseCaseRelationshipField,
89        right_ids: &[EntityId],
90    ) -> Result<(), RepositoryError>;
91    fn move_relationship_ids(
92        &mut self,
93        id: &EntityId,
94        field: &UseCaseRelationshipField,
95        ids_to_move: &[EntityId],
96        new_index: i32,
97    ) -> Result<Vec<EntityId>, RepositoryError>;
98}
99
100pub trait UseCaseTableRO {
101    fn get(&self, id: &EntityId) -> Result<Option<UseCase>, RepositoryError>;
102    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<UseCase>>, RepositoryError>;
103    fn get_all(&self) -> Result<Vec<UseCase>, RepositoryError>;
104    fn get_relationship(
105        &self,
106        id: &EntityId,
107        field: &UseCaseRelationshipField,
108    ) -> Result<Vec<EntityId>, RepositoryError>;
109    fn get_relationship_many(
110        &self,
111        ids: &[EntityId],
112        field: &UseCaseRelationshipField,
113    ) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError>;
114    fn get_relationship_count(
115        &self,
116        id: &EntityId,
117        field: &UseCaseRelationshipField,
118    ) -> Result<usize, RepositoryError>;
119    fn get_relationship_in_range(
120        &self,
121        id: &EntityId,
122        field: &UseCaseRelationshipField,
123        offset: usize,
124        limit: usize,
125    ) -> Result<Vec<EntityId>, RepositoryError>;
126    fn get_relationships_from_right_ids(
127        &self,
128        field: &UseCaseRelationshipField,
129        right_ids: &[EntityId],
130    ) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError>;
131}
132
133pub struct UseCaseRepository<'a> {
134    table: Box<dyn UseCaseTable + 'a>,
135    transaction: &'a Transaction,
136}
137
138impl<'a> UseCaseRepository<'a> {
139    pub fn new(table: Box<dyn UseCaseTable + 'a>, transaction: &'a Transaction) -> Self {
140        UseCaseRepository { table, transaction }
141    }
142
143    pub fn create_orphan(
144        &mut self,
145        event_buffer: &mut EventBuffer,
146        entity: &UseCase,
147    ) -> Result<UseCase, RepositoryError> {
148        let new = self.table.create(entity)?;
149        event_buffer.push(Event {
150            origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Created)),
151            ids: vec![new.id],
152            data: None,
153        });
154        Ok(new)
155    }
156
157    pub fn create_orphan_multi(
158        &mut self,
159        event_buffer: &mut EventBuffer,
160        entities: &[UseCase],
161    ) -> Result<Vec<UseCase>, RepositoryError> {
162        let new_entities = self.table.create_multi(entities)?;
163        event_buffer.push(Event {
164            origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Created)),
165            ids: new_entities.iter().map(|e| e.id).collect(),
166            data: None,
167        });
168        Ok(new_entities)
169    }
170    pub fn create(
171        &mut self,
172        event_buffer: &mut EventBuffer,
173        entity: &UseCase,
174        owner_id: EntityId,
175        index: i32,
176    ) -> Result<UseCase, RepositoryError> {
177        let new = self.table.create(entity)?;
178        let created_id = new.id;
179
180        let mut relationship_ids = self.get_relationships_from_owner(&owner_id)?;
181        // Insert at index
182        if index >= 0 && (index as usize) < relationship_ids.len() {
183            relationship_ids.insert(index as usize, created_id);
184        } else {
185            relationship_ids.push(created_id);
186        }
187
188        self.set_relationships_in_owner(event_buffer, &owner_id, &relationship_ids)?;
189        event_buffer.push(Event {
190            origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Created)),
191            ids: vec![created_id],
192            data: None,
193        });
194        Ok(new)
195    }
196
197    pub fn create_multi(
198        &mut self,
199        event_buffer: &mut EventBuffer,
200        entities: &[UseCase],
201        owner_id: EntityId,
202        index: i32,
203    ) -> Result<Vec<UseCase>, RepositoryError> {
204        let new_entities = self.table.create_multi(entities)?;
205        let created_ids: Vec<EntityId> = new_entities.iter().map(|e| e.id).collect();
206
207        let mut relationship_ids = self.get_relationships_from_owner(&owner_id)?;
208        if index >= 0 && (index as usize) < relationship_ids.len() {
209            for (i, id) in created_ids.iter().enumerate() {
210                relationship_ids.insert(index as usize + i, *id);
211            }
212        } else {
213            relationship_ids.extend(created_ids.iter());
214        }
215
216        self.set_relationships_in_owner(event_buffer, &owner_id, &relationship_ids)?;
217        event_buffer.push(Event {
218            origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Created)),
219            ids: created_ids,
220            data: None,
221        });
222        Ok(new_entities)
223    }
224
225    pub fn get(&self, id: &EntityId) -> Result<Option<UseCase>, RepositoryError> {
226        self.table.get(id)
227    }
228    pub fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<UseCase>>, RepositoryError> {
229        self.table.get_multi(ids)
230    }
231    pub fn get_all(&self) -> Result<Vec<UseCase>, RepositoryError> {
232        self.table.get_all()
233    }
234
235    pub fn update(
236        &mut self,
237        event_buffer: &mut EventBuffer,
238        entity: &UseCase,
239    ) -> Result<UseCase, RepositoryError> {
240        let updated = self.table.update(entity)?;
241        event_buffer.push(Event {
242            origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Updated)),
243            ids: vec![updated.id],
244            data: None,
245        });
246        Ok(updated)
247    }
248
249    pub fn update_multi(
250        &mut self,
251        event_buffer: &mut EventBuffer,
252        entities: &[UseCase],
253    ) -> Result<Vec<UseCase>, RepositoryError> {
254        let updated = self.table.update_multi(entities)?;
255        event_buffer.push(Event {
256            origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Updated)),
257            ids: updated.iter().map(|e| e.id).collect(),
258            data: None,
259        });
260        Ok(updated)
261    }
262
263    pub fn update_with_relationships(
264        &mut self,
265        event_buffer: &mut EventBuffer,
266        entity: &UseCase,
267    ) -> Result<UseCase, RepositoryError> {
268        let updated = self.table.update_with_relationships(entity)?;
269        event_buffer.push(Event {
270            origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Updated)),
271            ids: vec![updated.id],
272            data: None,
273        });
274        Ok(updated)
275    }
276
277    pub fn update_with_relationships_multi(
278        &mut self,
279        event_buffer: &mut EventBuffer,
280        entities: &[UseCase],
281    ) -> Result<Vec<UseCase>, RepositoryError> {
282        let updated = self.table.update_with_relationships_multi(entities)?;
283        event_buffer.push(Event {
284            origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Updated)),
285            ids: updated.iter().map(|e| e.id).collect(),
286            data: None,
287        });
288        Ok(updated)
289    }
290
291    pub fn remove(
292        &mut self,
293        event_buffer: &mut EventBuffer,
294        id: &EntityId,
295    ) -> Result<(), RepositoryError> {
296        let entity = match self.table.get(id)? {
297            Some(e) => e,
298            None => return Ok(()),
299        };
300        // get all strong forward relationship fields
301
302        let dto_in = entity.dto_in;
303        let dto_out = entity.dto_out;
304
305        // remove all strong relationships, initiating a cascade remove
306
307        if let Some(dto_in_id) = dto_in {
308            repository_factory::write::create_dto_repository(self.transaction)?
309                .remove(event_buffer, &dto_in_id)?;
310        }
311        if let Some(dto_out_id) = dto_out {
312            repository_factory::write::create_dto_repository(self.transaction)?
313                .remove(event_buffer, &dto_out_id)?;
314        }
315        // Before removal, find which owner(s) reference this entity
316        let affected_owner_ids: Vec<EntityId> = {
317            let owner_repo =
318                repository_factory::write::create_feature_repository(self.transaction)?;
319            owner_repo
320                .get_relationships_from_right_ids(&FeatureRelationshipField::UseCases, &[*id])?
321                .into_iter()
322                .map(|(owner_id, _)| owner_id)
323                .collect()
324        };
325        // Save each owner's current relationship IDs (properly ordered via get_relationships_from_owner)
326        let mut owner_rel_before: std::collections::HashMap<EntityId, Vec<EntityId>> =
327            std::collections::HashMap::new();
328        for owner_id in &affected_owner_ids {
329            owner_rel_before.insert(*owner_id, self.get_relationships_from_owner(owner_id)?);
330        }
331
332        // remove entity
333        self.table.remove(id)?;
334        event_buffer.push(Event {
335            origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Removed)),
336            ids: vec![*id],
337            data: None,
338        });
339        // Update each affected owner's relationship to exclude removed ID (emits Updated event)
340        for owner_id in &affected_owner_ids {
341            if let Some(rel_ids) = owner_rel_before.get(owner_id) {
342                let updated: Vec<EntityId> =
343                    rel_ids.iter().copied().filter(|rid| *rid != *id).collect();
344                self.set_relationships_in_owner(event_buffer, owner_id, &updated)?;
345            }
346        }
347
348        Ok(())
349    }
350
351    pub fn remove_multi(
352        &mut self,
353        event_buffer: &mut EventBuffer,
354        ids: &[EntityId],
355    ) -> Result<(), RepositoryError> {
356        let entities = self.table.get_multi(ids)?;
357        if entities.is_empty() || entities.iter().all(|e| e.is_none()) {
358            return Ok(());
359        }
360
361        // get all strong forward relationship fields
362
363        let dto_in_ids: Vec<EntityId> = entities
364            .iter()
365            .filter_map(|entity| entity.as_ref().and_then(|entity| entity.dto_in))
366            .collect();
367        let dto_out_ids: Vec<EntityId> = entities
368            .iter()
369            .filter_map(|entity| entity.as_ref().and_then(|entity| entity.dto_out))
370            .collect();
371
372        // remove all strong relationships, initiating a cascade remove
373
374        repository_factory::write::create_dto_repository(self.transaction)?
375            .remove_multi(event_buffer, &dto_in_ids)?;
376        repository_factory::write::create_dto_repository(self.transaction)?
377            .remove_multi(event_buffer, &dto_out_ids)?;
378        // Before removal, find which owner(s) reference these entities
379        let affected_owner_ids: Vec<EntityId> = {
380            let owner_repo =
381                repository_factory::write::create_feature_repository(self.transaction)?;
382            owner_repo
383                .get_relationships_from_right_ids(&FeatureRelationshipField::UseCases, ids)?
384                .into_iter()
385                .map(|(owner_id, _)| owner_id)
386                .collect()
387        };
388        // Save each owner's current relationship IDs (properly ordered via get_relationships_from_owner)
389        let mut owner_rel_before: std::collections::HashMap<EntityId, Vec<EntityId>> =
390            std::collections::HashMap::new();
391        for owner_id in &affected_owner_ids {
392            owner_rel_before.insert(*owner_id, self.get_relationships_from_owner(owner_id)?);
393        }
394
395        self.table.remove_multi(ids)?;
396        event_buffer.push(Event {
397            origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Removed)),
398            ids: ids.into(),
399            data: None,
400        });
401        // Update each affected owner's relationship to exclude removed IDs (emits Updated event)
402        {
403            let removed_set: std::collections::HashSet<EntityId> = ids.iter().copied().collect();
404            for owner_id in &affected_owner_ids {
405                if let Some(rel_ids) = owner_rel_before.get(owner_id) {
406                    let updated: Vec<EntityId> = rel_ids
407                        .iter()
408                        .copied()
409                        .filter(|rid| !removed_set.contains(rid))
410                        .collect();
411                    self.set_relationships_in_owner(event_buffer, owner_id, &updated)?;
412                }
413            }
414        }
415
416        Ok(())
417    }
418    pub fn get_relationship(
419        &self,
420        id: &EntityId,
421        field: &UseCaseRelationshipField,
422    ) -> Result<Vec<EntityId>, RepositoryError> {
423        self.table.get_relationship(id, field)
424    }
425    pub fn get_relationship_many(
426        &self,
427        ids: &[EntityId],
428        field: &UseCaseRelationshipField,
429    ) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError> {
430        self.table.get_relationship_many(ids, field)
431    }
432    pub fn get_relationship_count(
433        &self,
434        id: &EntityId,
435        field: &UseCaseRelationshipField,
436    ) -> Result<usize, RepositoryError> {
437        self.table.get_relationship_count(id, field)
438    }
439    pub fn get_relationship_in_range(
440        &self,
441        id: &EntityId,
442        field: &UseCaseRelationshipField,
443        offset: usize,
444        limit: usize,
445    ) -> Result<Vec<EntityId>, RepositoryError> {
446        self.table
447            .get_relationship_in_range(id, field, offset, limit)
448    }
449    pub fn get_relationships_from_right_ids(
450        &self,
451        field: &UseCaseRelationshipField,
452        right_ids: &[EntityId],
453    ) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError> {
454        self.table
455            .get_relationships_from_right_ids(field, right_ids)
456    }
457
458    pub fn set_relationship_multi(
459        &mut self,
460        event_buffer: &mut EventBuffer,
461        field: &UseCaseRelationshipField,
462        relationships: Vec<(EntityId, Vec<EntityId>)>,
463    ) -> Result<(), RepositoryError> {
464        // Validate that all right_ids exist
465        let all_right_ids: Vec<EntityId> = relationships
466            .iter()
467            .flat_map(|(_, ids)| ids.iter().copied())
468            .collect();
469        if !all_right_ids.is_empty() {
470            match field {
471                UseCaseRelationshipField::DtoIn => {
472                    let child_repo =
473                        repository_factory::write::create_dto_repository(self.transaction)?;
474                    let found = child_repo.get_multi(&all_right_ids)?;
475                    let missing: Vec<_> = all_right_ids
476                        .iter()
477                        .zip(found.iter())
478                        .filter(|(_, entity)| entity.is_none())
479                        .map(|(id, _)| *id)
480                        .collect();
481                    if !missing.is_empty() {
482                        return Err(RepositoryError::MissingRelationshipTarget {
483                            operation: "set_relationship_multi",
484                            ids: missing,
485                        });
486                    }
487                }
488                UseCaseRelationshipField::DtoOut => {
489                    let child_repo =
490                        repository_factory::write::create_dto_repository(self.transaction)?;
491                    let found = child_repo.get_multi(&all_right_ids)?;
492                    let missing: Vec<_> = all_right_ids
493                        .iter()
494                        .zip(found.iter())
495                        .filter(|(_, entity)| entity.is_none())
496                        .map(|(id, _)| *id)
497                        .collect();
498                    if !missing.is_empty() {
499                        return Err(RepositoryError::MissingRelationshipTarget {
500                            operation: "set_relationship_multi",
501                            ids: missing,
502                        });
503                    }
504                }
505                UseCaseRelationshipField::Entities => {
506                    let child_repo =
507                        repository_factory::write::create_entity_repository(self.transaction)?;
508                    let found = child_repo.get_multi(&all_right_ids)?;
509                    let missing: Vec<_> = all_right_ids
510                        .iter()
511                        .zip(found.iter())
512                        .filter(|(_, entity)| entity.is_none())
513                        .map(|(id, _)| *id)
514                        .collect();
515                    if !missing.is_empty() {
516                        return Err(RepositoryError::MissingRelationshipTarget {
517                            operation: "set_relationship_multi",
518                            ids: missing,
519                        });
520                    }
521                }
522            }
523        }
524        self.table
525            .set_relationship_multi(field, relationships.clone())?;
526        for (left_id, right_ids) in relationships {
527            event_buffer.push(Event {
528                origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Updated)),
529                ids: vec![left_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        }
541        Ok(())
542    }
543
544    pub fn set_relationship(
545        &mut self,
546        event_buffer: &mut EventBuffer,
547        id: &EntityId,
548        field: &UseCaseRelationshipField,
549        right_ids: &[EntityId],
550    ) -> Result<(), RepositoryError> {
551        // Validate that all right_ids exist
552        if !right_ids.is_empty() {
553            match field {
554                UseCaseRelationshipField::DtoIn => {
555                    let child_repo =
556                        repository_factory::write::create_dto_repository(self.transaction)?;
557                    let found = child_repo.get_multi(right_ids)?;
558                    let missing: Vec<_> = right_ids
559                        .iter()
560                        .zip(found.iter())
561                        .filter(|(_, entity)| entity.is_none())
562                        .map(|(id, _)| *id)
563                        .collect();
564                    if !missing.is_empty() {
565                        return Err(RepositoryError::MissingRelationshipTarget {
566                            operation: "set_relationship",
567                            ids: missing,
568                        });
569                    }
570                }
571                UseCaseRelationshipField::DtoOut => {
572                    let child_repo =
573                        repository_factory::write::create_dto_repository(self.transaction)?;
574                    let found = child_repo.get_multi(right_ids)?;
575                    let missing: Vec<_> = right_ids
576                        .iter()
577                        .zip(found.iter())
578                        .filter(|(_, entity)| entity.is_none())
579                        .map(|(id, _)| *id)
580                        .collect();
581                    if !missing.is_empty() {
582                        return Err(RepositoryError::MissingRelationshipTarget {
583                            operation: "set_relationship",
584                            ids: missing,
585                        });
586                    }
587                }
588                UseCaseRelationshipField::Entities => {
589                    let child_repo =
590                        repository_factory::write::create_entity_repository(self.transaction)?;
591                    let found = child_repo.get_multi(right_ids)?;
592                    let missing: Vec<_> = right_ids
593                        .iter()
594                        .zip(found.iter())
595                        .filter(|(_, entity)| entity.is_none())
596                        .map(|(id, _)| *id)
597                        .collect();
598                    if !missing.is_empty() {
599                        return Err(RepositoryError::MissingRelationshipTarget {
600                            operation: "set_relationship",
601                            ids: missing,
602                        });
603                    }
604                }
605            }
606        }
607        self.table.set_relationship(id, field, right_ids)?;
608        event_buffer.push(Event {
609            origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Updated)),
610            ids: vec![*id],
611            data: Some(format!(
612                "{}:{}",
613                field,
614                right_ids
615                    .iter()
616                    .map(|id| id.to_string())
617                    .collect::<Vec<_>>()
618                    .join(",")
619            )),
620        });
621        Ok(())
622    }
623
624    pub fn move_relationship_ids(
625        &mut self,
626        event_buffer: &mut EventBuffer,
627        id: &EntityId,
628        field: &UseCaseRelationshipField,
629        ids_to_move: &[EntityId],
630        new_index: i32,
631    ) -> Result<Vec<EntityId>, RepositoryError> {
632        let reordered = self
633            .table
634            .move_relationship_ids(id, field, ids_to_move, new_index)?;
635        event_buffer.push(Event {
636            origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Updated)),
637            ids: vec![*id],
638            data: Some(format!(
639                "{}:{}",
640                field,
641                reordered
642                    .iter()
643                    .map(|id| id.to_string())
644                    .collect::<Vec<_>>()
645                    .join(",")
646            )),
647        });
648        Ok(reordered)
649    }
650    pub fn get_relationships_from_owner(
651        &self,
652        owner_id: &EntityId,
653    ) -> Result<Vec<EntityId>, RepositoryError> {
654        let repo = repository_factory::write::create_feature_repository(self.transaction)?;
655        repo.get_relationship(owner_id, &FeatureRelationshipField::UseCases)
656    }
657
658    pub fn set_relationships_in_owner(
659        &mut self,
660        event_buffer: &mut EventBuffer,
661        owner_id: &EntityId,
662        ids: &[EntityId],
663    ) -> Result<(), RepositoryError> {
664        let mut repo = repository_factory::write::create_feature_repository(self.transaction)?;
665        repo.set_relationship(
666            event_buffer,
667            owner_id,
668            &FeatureRelationshipField::UseCases,
669            ids,
670        )
671    }
672
673    pub fn snapshot(&self, ids: &[EntityId]) -> Result<EntityTreeSnapshot, RepositoryError> {
674        // O(1) whole-store capture (im::HashMap structural sharing). Restore is scoped to `ids`.
675        let store_snap = self.transaction.snapshot_store();
676        Ok(EntityTreeSnapshot {
677            store_snapshot: Some(store_snap),
678            root_ids: ids.to_vec(),
679        })
680    }
681
682    pub fn restore(
683        &mut self,
684        event_buffer: &mut EventBuffer,
685        snap: &EntityTreeSnapshot,
686    ) -> Result<(), RepositoryError> {
687        let store_snap = snap
688            .store_snapshot
689            .as_ref()
690            .ok_or_else(|| RepositoryError::Serialization("missing store snapshot".into()))?;
691        let snap_store = store_snap
692            .downcast_ref::<HashMapStoreSnapshot>()
693            .ok_or_else(|| RepositoryError::Serialization("snapshot type mismatch".into()))?;
694
695        // Scoped restore: walk the subtree rooted at the captured ids (strong relationships only),
696        // in both the snapshot and the live store, and reconcile only it.
697        let mut visited = std::collections::HashSet::new();
698        self.restore_subtree(event_buffer, snap_store, &snap.root_ids, &mut visited)?;
699
700        // Surgically reconcile the root ids' placement in their (external) strong owner,
701        // preserving sibling edits made on other undo stacks.
702        self.reconcile_backref_feature_use_cases(event_buffer, snap_store, &snap.root_ids)?;
703
704        Ok(())
705    }
706
707    /// Restore the subtree rooted at `ids` (of this entity type) to the snapshot state, recursing
708    /// into strong children. Touches only the subtree: in-scope rows and the forward junctions they
709    /// own are restored wholesale; external referrers are reconciled surgically; everything else
710    /// (other trunks, non-undoable data) is left untouched. `visited` guards against cycles.
711    pub(crate) fn restore_subtree(
712        &self,
713        event_buffer: &mut EventBuffer,
714        snap: &HashMapStoreSnapshot,
715        ids: &[EntityId],
716        // Keyed by (entity type, id): EntityIds are per-type, not globally unique, so a bare
717        // id set would let one entity type's id falsely mask another's.
718        visited: &mut std::collections::HashSet<(&'static str, EntityId)>,
719    ) -> Result<(), RepositoryError> {
720        let ids: Vec<EntityId> = ids
721            .iter()
722            .copied()
723            .filter(|id| visited.insert(("use_case", *id)))
724            .collect();
725        if ids.is_empty() {
726            return Ok(());
727        }
728
729        let store = self.transaction.get_store();
730
731        // Three-way partition by presence in snapshot vs live store (read before mutating).
732        let mut to_create: Vec<EntityId> = Vec::new(); // in snapshot, gone from live -> re-add
733        let mut to_update: Vec<EntityId> = Vec::new(); // in both                    -> revert
734        let mut to_delete: Vec<EntityId> = Vec::new(); // live only (created after)  -> delete
735        {
736            let live = read_or_recover(&store.use_cases);
737            for id in &ids {
738                match (snap.use_cases.contains_key(id), live.contains_key(id)) {
739                    (true, false) => to_create.push(*id),
740                    (true, true) => to_update.push(*id),
741                    (false, true) => to_delete.push(*id),
742                    (false, false) => {}
743                }
744            }
745        }
746
747        // 1. Recurse into strong children first (collect child ids from snapshot + live, then drop
748        //    all locks before recursing so self-referential ownership cannot deadlock).
749        {
750            let mut child_ids: Vec<EntityId> = Vec::new();
751            for id in to_create.iter().chain(to_update.iter()) {
752                if let Some(list) = snap.jn_dto_from_use_case_dto_in.get(id) {
753                    child_ids.extend(list.iter().copied());
754                }
755            }
756            {
757                let live_jn = read_or_recover(&store.jn_dto_from_use_case_dto_in);
758                for id in &ids {
759                    if let Some(list) = live_jn.get(id) {
760                        child_ids.extend(list.iter().copied());
761                    }
762                }
763            }
764            child_ids.sort();
765            child_ids.dedup();
766            if !child_ids.is_empty() {
767                repository_factory::write::create_dto_repository(self.transaction)?
768                    .restore_subtree(event_buffer, snap, &child_ids, visited)?;
769            }
770        }
771        {
772            let mut child_ids: Vec<EntityId> = Vec::new();
773            for id in to_create.iter().chain(to_update.iter()) {
774                if let Some(list) = snap.jn_dto_from_use_case_dto_out.get(id) {
775                    child_ids.extend(list.iter().copied());
776                }
777            }
778            {
779                let live_jn = read_or_recover(&store.jn_dto_from_use_case_dto_out);
780                for id in &ids {
781                    if let Some(list) = live_jn.get(id) {
782                        child_ids.extend(list.iter().copied());
783                    }
784                }
785            }
786            child_ids.sort();
787            child_ids.dedup();
788            if !child_ids.is_empty() {
789                repository_factory::write::create_dto_repository(self.transaction)?
790                    .restore_subtree(event_buffer, snap, &child_ids, visited)?;
791            }
792        }
793
794        // 2. Entity rows: revert/re-add from snapshot, delete the ones created after it.
795        {
796            let mut live = write_or_recover(&store.use_cases);
797            for id in to_create.iter().chain(to_update.iter()) {
798                if let Some(row) = snap.use_cases.get(id) {
799                    live.insert(*id, row.clone());
800                }
801            }
802            for id in &to_delete {
803                live.remove(id);
804            }
805        }
806
807        // 3. This entity's own forward junctions (strong + weak): restored wholesale because the
808        //    junction key is in-scope (owned exclusively by this trunk).
809        {
810            let mut live_jn = write_or_recover(&store.jn_dto_from_use_case_dto_in);
811            for id in to_create.iter().chain(to_update.iter()) {
812                match snap.jn_dto_from_use_case_dto_in.get(id) {
813                    Some(v) => {
814                        live_jn.insert(*id, v.clone());
815                    }
816                    None => {
817                        live_jn.remove(id);
818                    }
819                }
820            }
821            for id in &to_delete {
822                live_jn.remove(id);
823            }
824        }
825        {
826            let mut live_jn = write_or_recover(&store.jn_dto_from_use_case_dto_out);
827            for id in to_create.iter().chain(to_update.iter()) {
828                match snap.jn_dto_from_use_case_dto_out.get(id) {
829                    Some(v) => {
830                        live_jn.insert(*id, v.clone());
831                    }
832                    None => {
833                        live_jn.remove(id);
834                    }
835                }
836            }
837            for id in &to_delete {
838                live_jn.remove(id);
839            }
840        }
841        {
842            let mut live_jn = write_or_recover(&store.jn_entity_from_use_case_entities);
843            for id in to_create.iter().chain(to_update.iter()) {
844                match snap.jn_entity_from_use_case_entities.get(id) {
845                    Some(v) => {
846                        live_jn.insert(*id, v.clone());
847                    }
848                    None => {
849                        live_jn.remove(id);
850                    }
851                }
852            }
853            for id in &to_delete {
854                live_jn.remove(id);
855            }
856        }
857
858        // 4. External weak referrers of these ids: reconcile surgically (membership only).
859        self.reconcile_backref_file_use_case(event_buffer, snap, &ids)?;
860
861        // 5. Events: precise per-id (Created/Updated/Removed), not a whole-store storm.
862        if !to_create.is_empty() {
863            event_buffer.push(Event {
864                origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Created)),
865                ids: to_create.clone(),
866                data: None,
867            });
868            event_buffer.push(Event {
869                origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Updated)),
870                ids: to_create.clone(),
871                data: None,
872            });
873        }
874        if !to_update.is_empty() {
875            event_buffer.push(Event {
876                origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Updated)),
877                ids: to_update.clone(),
878                data: None,
879            });
880        }
881        if !to_delete.is_empty() {
882            event_buffer.push(Event {
883                origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Removed)),
884                ids: to_delete.clone(),
885                data: None,
886            });
887        }
888
889        Ok(())
890    }
891
892    /// Surgically reconcile one external referrer junction (out-of-scope key -> in-scope values):
893    /// `file.use_case`.
894    fn reconcile_backref_file_use_case(
895        &self,
896        event_buffer: &mut EventBuffer,
897        snap: &HashMapStoreSnapshot,
898        scope_ids: &[EntityId],
899    ) -> Result<(), RepositoryError> {
900        let scope: std::collections::HashSet<EntityId> = scope_ids.iter().copied().collect();
901        if scope.is_empty() {
902            return Ok(());
903        }
904        let store = self.transaction.get_store();
905
906        // External left keys whose ordered list references any scope id, in snapshot or live.
907        let mut left_keys: std::collections::HashSet<EntityId> = std::collections::HashSet::new();
908        for (left, rights) in snap.jn_use_case_from_file_use_case.iter() {
909            if rights.iter().any(|rid| scope.contains(rid)) {
910                left_keys.insert(*left);
911            }
912        }
913        {
914            let live_jn = read_or_recover(&store.jn_use_case_from_file_use_case);
915            for (left, rights) in live_jn.iter() {
916                if rights.iter().any(|rid| scope.contains(rid)) {
917                    left_keys.insert(*left);
918                }
919            }
920        }
921
922        for left in left_keys {
923            let snap_list: Vec<EntityId> = snap
924                .jn_use_case_from_file_use_case
925                .get(&left)
926                .cloned()
927                .unwrap_or_default();
928            let new_list = {
929                let live_jn = read_or_recover(&store.jn_use_case_from_file_use_case);
930                let live_list: Vec<EntityId> = live_jn.get(&left).cloned().unwrap_or_default();
931                let reconciled = crate::database::hashmap_store::reconcile_backref_list(
932                    &live_list, &snap_list, &scope,
933                );
934                if reconciled == live_list {
935                    None
936                } else {
937                    Some(reconciled)
938                }
939            };
940            if let Some(reconciled) = new_list {
941                write_or_recover(&store.jn_use_case_from_file_use_case).insert(left, reconciled);
942                event_buffer.push(Event {
943                    origin: Origin::DirectAccess(DirectAccessEntity::File(EntityEvent::Updated)),
944                    ids: vec![left],
945                    data: None,
946                });
947            }
948        }
949        Ok(())
950    }
951
952    /// Surgically reconcile one external referrer junction (out-of-scope key -> in-scope values):
953    /// `feature.use_cases`.
954    fn reconcile_backref_feature_use_cases(
955        &self,
956        event_buffer: &mut EventBuffer,
957        snap: &HashMapStoreSnapshot,
958        scope_ids: &[EntityId],
959    ) -> Result<(), RepositoryError> {
960        let scope: std::collections::HashSet<EntityId> = scope_ids.iter().copied().collect();
961        if scope.is_empty() {
962            return Ok(());
963        }
964        let store = self.transaction.get_store();
965
966        // External left keys whose ordered list references any scope id, in snapshot or live.
967        let mut left_keys: std::collections::HashSet<EntityId> = std::collections::HashSet::new();
968        for (left, rights) in snap.jn_use_case_from_feature_use_cases.iter() {
969            if rights.iter().any(|rid| scope.contains(rid)) {
970                left_keys.insert(*left);
971            }
972        }
973        {
974            let live_jn = read_or_recover(&store.jn_use_case_from_feature_use_cases);
975            for (left, rights) in live_jn.iter() {
976                if rights.iter().any(|rid| scope.contains(rid)) {
977                    left_keys.insert(*left);
978                }
979            }
980        }
981
982        for left in left_keys {
983            let snap_list: Vec<EntityId> = snap
984                .jn_use_case_from_feature_use_cases
985                .get(&left)
986                .cloned()
987                .unwrap_or_default();
988            let new_list = {
989                let live_jn = read_or_recover(&store.jn_use_case_from_feature_use_cases);
990                let live_list: Vec<EntityId> = live_jn.get(&left).cloned().unwrap_or_default();
991                let reconciled = crate::database::hashmap_store::reconcile_backref_list(
992                    &live_list, &snap_list, &scope,
993                );
994                if reconciled == live_list {
995                    None
996                } else {
997                    Some(reconciled)
998                }
999            };
1000            if let Some(reconciled) = new_list {
1001                write_or_recover(&store.jn_use_case_from_feature_use_cases)
1002                    .insert(left, reconciled);
1003                event_buffer.push(Event {
1004                    origin: Origin::DirectAccess(DirectAccessEntity::Feature(EntityEvent::Updated)),
1005                    ids: vec![left],
1006                    data: None,
1007                });
1008            }
1009        }
1010        Ok(())
1011    }
1012}
1013
1014pub struct UseCaseRepositoryRO<'a> {
1015    table: Box<dyn UseCaseTableRO + 'a>,
1016}
1017impl<'a> UseCaseRepositoryRO<'a> {
1018    pub fn new(table: Box<dyn UseCaseTableRO + 'a>) -> Self {
1019        UseCaseRepositoryRO { table }
1020    }
1021    pub fn get(&self, id: &EntityId) -> Result<Option<UseCase>, RepositoryError> {
1022        self.table.get(id)
1023    }
1024    pub fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<UseCase>>, RepositoryError> {
1025        self.table.get_multi(ids)
1026    }
1027    pub fn get_all(&self) -> Result<Vec<UseCase>, RepositoryError> {
1028        self.table.get_all()
1029    }
1030    pub fn get_relationship(
1031        &self,
1032        id: &EntityId,
1033        field: &UseCaseRelationshipField,
1034    ) -> Result<Vec<EntityId>, RepositoryError> {
1035        self.table.get_relationship(id, field)
1036    }
1037    pub fn get_relationship_many(
1038        &self,
1039        ids: &[EntityId],
1040        field: &UseCaseRelationshipField,
1041    ) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError> {
1042        self.table.get_relationship_many(ids, field)
1043    }
1044    pub fn get_relationship_count(
1045        &self,
1046        id: &EntityId,
1047        field: &UseCaseRelationshipField,
1048    ) -> Result<usize, RepositoryError> {
1049        self.table.get_relationship_count(id, field)
1050    }
1051    pub fn get_relationship_in_range(
1052        &self,
1053        id: &EntityId,
1054        field: &UseCaseRelationshipField,
1055        offset: usize,
1056        limit: usize,
1057    ) -> Result<Vec<EntityId>, RepositoryError> {
1058        self.table
1059            .get_relationship_in_range(id, field, offset, limit)
1060    }
1061    pub fn get_relationships_from_right_ids(
1062        &self,
1063        field: &UseCaseRelationshipField,
1064        right_ids: &[EntityId],
1065    ) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError> {
1066        self.table
1067            .get_relationships_from_right_ids(field, right_ids)
1068    }
1069}