Skip to main content

common/direct_access/user_interface/
user_interface_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::UserInterface,
16    event::{DirectAccessEntity, EntityEvent, Event, EventBuffer, Origin},
17    snapshot::EntityTreeSnapshot,
18    types::EntityId,
19};
20
21use crate::direct_access::workspace::WorkspaceRelationshipField;
22use crate::error::RepositoryError;
23use serde::{Deserialize, Serialize};
24
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26pub enum UserInterfaceRelationshipField {}
27
28impl Display for UserInterfaceRelationshipField {
29    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
30        write!(f, "{:?}", self)
31    }
32}
33
34pub trait UserInterfaceTable {
35    fn create(&mut self, entity: &UserInterface) -> Result<UserInterface, RepositoryError>;
36    fn create_multi(
37        &mut self,
38        entities: &[UserInterface],
39    ) -> Result<Vec<UserInterface>, RepositoryError>;
40    fn get(&self, id: &EntityId) -> Result<Option<UserInterface>, RepositoryError>;
41    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<UserInterface>>, RepositoryError>;
42    fn get_all(&self) -> Result<Vec<UserInterface>, RepositoryError>;
43    fn update(&mut self, entity: &UserInterface) -> Result<UserInterface, RepositoryError>;
44    fn update_multi(
45        &mut self,
46        entities: &[UserInterface],
47    ) -> Result<Vec<UserInterface>, RepositoryError>;
48    fn update_with_relationships(
49        &mut self,
50        entity: &UserInterface,
51    ) -> Result<UserInterface, RepositoryError>;
52    fn update_with_relationships_multi(
53        &mut self,
54        entities: &[UserInterface],
55    ) -> Result<Vec<UserInterface>, RepositoryError>;
56    fn remove(&mut self, id: &EntityId) -> Result<(), RepositoryError>;
57    fn remove_multi(&mut self, ids: &[EntityId]) -> Result<(), RepositoryError>;
58}
59
60pub trait UserInterfaceTableRO {
61    fn get(&self, id: &EntityId) -> Result<Option<UserInterface>, RepositoryError>;
62    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<UserInterface>>, RepositoryError>;
63    fn get_all(&self) -> Result<Vec<UserInterface>, RepositoryError>;
64}
65
66pub struct UserInterfaceRepository<'a> {
67    table: Box<dyn UserInterfaceTable + 'a>,
68    transaction: &'a Transaction,
69}
70
71impl<'a> UserInterfaceRepository<'a> {
72    pub fn new(table: Box<dyn UserInterfaceTable + 'a>, transaction: &'a Transaction) -> Self {
73        UserInterfaceRepository { table, transaction }
74    }
75
76    pub fn create_orphan(
77        &mut self,
78        event_buffer: &mut EventBuffer,
79        entity: &UserInterface,
80    ) -> Result<UserInterface, RepositoryError> {
81        let new = self.table.create(entity)?;
82        event_buffer.push(Event {
83            origin: Origin::DirectAccess(DirectAccessEntity::UserInterface(EntityEvent::Created)),
84            ids: vec![new.id],
85            data: None,
86        });
87        Ok(new)
88    }
89
90    pub fn create_orphan_multi(
91        &mut self,
92        event_buffer: &mut EventBuffer,
93        entities: &[UserInterface],
94    ) -> Result<Vec<UserInterface>, RepositoryError> {
95        let new_entities = self.table.create_multi(entities)?;
96        event_buffer.push(Event {
97            origin: Origin::DirectAccess(DirectAccessEntity::UserInterface(EntityEvent::Created)),
98            ids: new_entities.iter().map(|e| e.id).collect(),
99            data: None,
100        });
101        Ok(new_entities)
102    }
103    pub fn create(
104        &mut self,
105        event_buffer: &mut EventBuffer,
106        entity: &UserInterface,
107        owner_id: EntityId,
108        _index: i32,
109    ) -> Result<UserInterface, RepositoryError> {
110        let new = self.table.create(entity)?;
111        let created_id = new.id;
112
113        let mut relationship_ids = self.get_relationships_from_owner(&owner_id)?;
114        if relationship_ids.is_empty() {
115            relationship_ids = vec![created_id];
116        } else {
117            // Replace existing relationship: cascade-remove old entities first
118            self.remove_multi(event_buffer, &relationship_ids)?;
119            relationship_ids = vec![created_id];
120        }
121
122        self.set_relationships_in_owner(event_buffer, &owner_id, &relationship_ids)?;
123        event_buffer.push(Event {
124            origin: Origin::DirectAccess(DirectAccessEntity::UserInterface(EntityEvent::Created)),
125            ids: vec![created_id],
126            data: None,
127        });
128        Ok(new)
129    }
130
131    pub fn create_multi(
132        &mut self,
133        event_buffer: &mut EventBuffer,
134        entities: &[UserInterface],
135        owner_id: EntityId,
136        _index: i32,
137    ) -> Result<Vec<UserInterface>, RepositoryError> {
138        let new_entities = self.table.create_multi(entities)?;
139        let created_ids: Vec<EntityId> = new_entities.iter().map(|e| e.id).collect();
140
141        let mut relationship_ids = self.get_relationships_from_owner(&owner_id)?;
142        if relationship_ids.is_empty() {
143            relationship_ids = created_ids.clone();
144        } else {
145            self.remove_multi(event_buffer, &relationship_ids)?;
146            relationship_ids = created_ids.clone();
147        }
148
149        self.set_relationships_in_owner(event_buffer, &owner_id, &relationship_ids)?;
150        event_buffer.push(Event {
151            origin: Origin::DirectAccess(DirectAccessEntity::UserInterface(EntityEvent::Created)),
152            ids: created_ids,
153            data: None,
154        });
155        Ok(new_entities)
156    }
157
158    pub fn get(&self, id: &EntityId) -> Result<Option<UserInterface>, RepositoryError> {
159        self.table.get(id)
160    }
161    pub fn get_multi(
162        &self,
163        ids: &[EntityId],
164    ) -> Result<Vec<Option<UserInterface>>, RepositoryError> {
165        self.table.get_multi(ids)
166    }
167    pub fn get_all(&self) -> Result<Vec<UserInterface>, RepositoryError> {
168        self.table.get_all()
169    }
170
171    pub fn update(
172        &mut self,
173        event_buffer: &mut EventBuffer,
174        entity: &UserInterface,
175    ) -> Result<UserInterface, RepositoryError> {
176        let updated = self.table.update(entity)?;
177        event_buffer.push(Event {
178            origin: Origin::DirectAccess(DirectAccessEntity::UserInterface(EntityEvent::Updated)),
179            ids: vec![updated.id],
180            data: None,
181        });
182        Ok(updated)
183    }
184
185    pub fn update_multi(
186        &mut self,
187        event_buffer: &mut EventBuffer,
188        entities: &[UserInterface],
189    ) -> Result<Vec<UserInterface>, RepositoryError> {
190        let updated = self.table.update_multi(entities)?;
191        event_buffer.push(Event {
192            origin: Origin::DirectAccess(DirectAccessEntity::UserInterface(EntityEvent::Updated)),
193            ids: updated.iter().map(|e| e.id).collect(),
194            data: None,
195        });
196        Ok(updated)
197    }
198
199    pub fn update_with_relationships(
200        &mut self,
201        event_buffer: &mut EventBuffer,
202        entity: &UserInterface,
203    ) -> Result<UserInterface, RepositoryError> {
204        let updated = self.table.update_with_relationships(entity)?;
205        event_buffer.push(Event {
206            origin: Origin::DirectAccess(DirectAccessEntity::UserInterface(EntityEvent::Updated)),
207            ids: vec![updated.id],
208            data: None,
209        });
210        Ok(updated)
211    }
212
213    pub fn update_with_relationships_multi(
214        &mut self,
215        event_buffer: &mut EventBuffer,
216        entities: &[UserInterface],
217    ) -> Result<Vec<UserInterface>, RepositoryError> {
218        let updated = self.table.update_with_relationships_multi(entities)?;
219        event_buffer.push(Event {
220            origin: Origin::DirectAccess(DirectAccessEntity::UserInterface(EntityEvent::Updated)),
221            ids: updated.iter().map(|e| e.id).collect(),
222            data: None,
223        });
224        Ok(updated)
225    }
226
227    pub fn remove(
228        &mut self,
229        event_buffer: &mut EventBuffer,
230        id: &EntityId,
231    ) -> Result<(), RepositoryError> {
232        let _entity = match self.table.get(id)? {
233            Some(e) => e,
234            None => return Ok(()),
235        };
236        // get all strong forward relationship fields
237
238        // remove all strong relationships, initiating a cascade remove
239
240        // Before removal, find which owner(s) reference this entity
241        let affected_owner_ids: Vec<EntityId> = {
242            let owner_repo =
243                repository_factory::write::create_workspace_repository(self.transaction)?;
244            owner_repo
245                .get_relationships_from_right_ids(
246                    &WorkspaceRelationshipField::UserInterface,
247                    &[*id],
248                )?
249                .into_iter()
250                .map(|(owner_id, _)| owner_id)
251                .collect()
252        };
253        // Save each owner's current relationship IDs (properly ordered via get_relationships_from_owner)
254        let mut owner_rel_before: std::collections::HashMap<EntityId, Vec<EntityId>> =
255            std::collections::HashMap::new();
256        for owner_id in &affected_owner_ids {
257            owner_rel_before.insert(*owner_id, self.get_relationships_from_owner(owner_id)?);
258        }
259
260        // remove entity
261        self.table.remove(id)?;
262        event_buffer.push(Event {
263            origin: Origin::DirectAccess(DirectAccessEntity::UserInterface(EntityEvent::Removed)),
264            ids: vec![*id],
265            data: None,
266        });
267        // Update each affected owner's relationship to exclude removed ID (emits Updated event)
268        for owner_id in &affected_owner_ids {
269            if let Some(rel_ids) = owner_rel_before.get(owner_id) {
270                let updated: Vec<EntityId> =
271                    rel_ids.iter().copied().filter(|rid| *rid != *id).collect();
272                self.set_relationships_in_owner(event_buffer, owner_id, &updated)?;
273            }
274        }
275
276        Ok(())
277    }
278
279    pub fn remove_multi(
280        &mut self,
281        event_buffer: &mut EventBuffer,
282        ids: &[EntityId],
283    ) -> Result<(), RepositoryError> {
284        let entities = self.table.get_multi(ids)?;
285        if entities.is_empty() || entities.iter().all(|e| e.is_none()) {
286            return Ok(());
287        }
288
289        // get all strong forward relationship fields
290
291        // remove all strong relationships, initiating a cascade remove
292
293        // Before removal, find which owner(s) reference these entities
294        let affected_owner_ids: Vec<EntityId> = {
295            let owner_repo =
296                repository_factory::write::create_workspace_repository(self.transaction)?;
297            owner_repo
298                .get_relationships_from_right_ids(&WorkspaceRelationshipField::UserInterface, ids)?
299                .into_iter()
300                .map(|(owner_id, _)| owner_id)
301                .collect()
302        };
303        // Save each owner's current relationship IDs (properly ordered via get_relationships_from_owner)
304        let mut owner_rel_before: std::collections::HashMap<EntityId, Vec<EntityId>> =
305            std::collections::HashMap::new();
306        for owner_id in &affected_owner_ids {
307            owner_rel_before.insert(*owner_id, self.get_relationships_from_owner(owner_id)?);
308        }
309
310        self.table.remove_multi(ids)?;
311        event_buffer.push(Event {
312            origin: Origin::DirectAccess(DirectAccessEntity::UserInterface(EntityEvent::Removed)),
313            ids: ids.into(),
314            data: None,
315        });
316        // Update each affected owner's relationship to exclude removed IDs (emits Updated event)
317        {
318            let removed_set: std::collections::HashSet<EntityId> = ids.iter().copied().collect();
319            for owner_id in &affected_owner_ids {
320                if let Some(rel_ids) = owner_rel_before.get(owner_id) {
321                    let updated: Vec<EntityId> = rel_ids
322                        .iter()
323                        .copied()
324                        .filter(|rid| !removed_set.contains(rid))
325                        .collect();
326                    self.set_relationships_in_owner(event_buffer, owner_id, &updated)?;
327                }
328            }
329        }
330
331        Ok(())
332    }
333    pub fn get_relationships_from_owner(
334        &self,
335        owner_id: &EntityId,
336    ) -> Result<Vec<EntityId>, RepositoryError> {
337        let repo = repository_factory::write::create_workspace_repository(self.transaction)?;
338        repo.get_relationship(owner_id, &WorkspaceRelationshipField::UserInterface)
339    }
340
341    pub fn set_relationships_in_owner(
342        &mut self,
343        event_buffer: &mut EventBuffer,
344        owner_id: &EntityId,
345        ids: &[EntityId],
346    ) -> Result<(), RepositoryError> {
347        let mut repo = repository_factory::write::create_workspace_repository(self.transaction)?;
348        repo.set_relationship(
349            event_buffer,
350            owner_id,
351            &WorkspaceRelationshipField::UserInterface,
352            ids,
353        )
354    }
355
356    pub fn snapshot(&self, ids: &[EntityId]) -> Result<EntityTreeSnapshot, RepositoryError> {
357        // O(1) whole-store capture (im::HashMap structural sharing). Restore is scoped to `ids`.
358        let store_snap = self.transaction.snapshot_store();
359        Ok(EntityTreeSnapshot {
360            store_snapshot: Some(store_snap),
361            root_ids: ids.to_vec(),
362        })
363    }
364
365    pub fn restore(
366        &mut self,
367        event_buffer: &mut EventBuffer,
368        snap: &EntityTreeSnapshot,
369    ) -> Result<(), RepositoryError> {
370        let store_snap = snap
371            .store_snapshot
372            .as_ref()
373            .ok_or_else(|| RepositoryError::Serialization("missing store snapshot".into()))?;
374        let snap_store = store_snap
375            .downcast_ref::<HashMapStoreSnapshot>()
376            .ok_or_else(|| RepositoryError::Serialization("snapshot type mismatch".into()))?;
377
378        // Scoped restore: walk the subtree rooted at the captured ids (strong relationships only),
379        // in both the snapshot and the live store, and reconcile only it.
380        let mut visited = std::collections::HashSet::new();
381        self.restore_subtree(event_buffer, snap_store, &snap.root_ids, &mut visited)?;
382
383        // Surgically reconcile the root ids' placement in their (external) strong owner,
384        // preserving sibling edits made on other undo stacks.
385        self.reconcile_backref_workspace_user_interface(event_buffer, snap_store, &snap.root_ids)?;
386
387        Ok(())
388    }
389
390    /// Restore the subtree rooted at `ids` (of this entity type) to the snapshot state, recursing
391    /// into strong children. Touches only the subtree: in-scope rows and the forward junctions they
392    /// own are restored wholesale; external referrers are reconciled surgically; everything else
393    /// (other trunks, non-undoable data) is left untouched. `visited` guards against cycles.
394    pub(crate) fn restore_subtree(
395        &self,
396        event_buffer: &mut EventBuffer,
397        snap: &HashMapStoreSnapshot,
398        ids: &[EntityId],
399        // Keyed by (entity type, id): EntityIds are per-type, not globally unique, so a bare
400        // id set would let one entity type's id falsely mask another's.
401        visited: &mut std::collections::HashSet<(&'static str, EntityId)>,
402    ) -> Result<(), RepositoryError> {
403        let ids: Vec<EntityId> = ids
404            .iter()
405            .copied()
406            .filter(|id| visited.insert(("user_interface", *id)))
407            .collect();
408        if ids.is_empty() {
409            return Ok(());
410        }
411
412        let store = self.transaction.get_store();
413
414        // Three-way partition by presence in snapshot vs live store (read before mutating).
415        let mut to_create: Vec<EntityId> = Vec::new(); // in snapshot, gone from live -> re-add
416        let mut to_update: Vec<EntityId> = Vec::new(); // in both                    -> revert
417        let mut to_delete: Vec<EntityId> = Vec::new(); // live only (created after)  -> delete
418        {
419            let live = read_or_recover(&store.user_interfaces);
420            for id in &ids {
421                match (snap.user_interfaces.contains_key(id), live.contains_key(id)) {
422                    (true, false) => to_create.push(*id),
423                    (true, true) => to_update.push(*id),
424                    (false, true) => to_delete.push(*id),
425                    (false, false) => {}
426                }
427            }
428        }
429
430        // 1. Recurse into strong children first (collect child ids from snapshot + live, then drop
431        //    all locks before recursing so self-referential ownership cannot deadlock).
432
433        // 2. Entity rows: revert/re-add from snapshot, delete the ones created after it.
434        {
435            let mut live = write_or_recover(&store.user_interfaces);
436            for id in to_create.iter().chain(to_update.iter()) {
437                if let Some(row) = snap.user_interfaces.get(id) {
438                    live.insert(*id, row.clone());
439                }
440            }
441            for id in &to_delete {
442                live.remove(id);
443            }
444        }
445
446        // 3. This entity's own forward junctions (strong + weak): restored wholesale because the
447        //    junction key is in-scope (owned exclusively by this trunk).
448
449        // 4. External weak referrers of these ids: reconcile surgically (membership only).
450
451        // 5. Events: precise per-id (Created/Updated/Removed), not a whole-store storm.
452        if !to_create.is_empty() {
453            event_buffer.push(Event {
454                origin: Origin::DirectAccess(DirectAccessEntity::UserInterface(
455                    EntityEvent::Created,
456                )),
457                ids: to_create.clone(),
458                data: None,
459            });
460        }
461        if !to_update.is_empty() {
462            event_buffer.push(Event {
463                origin: Origin::DirectAccess(DirectAccessEntity::UserInterface(
464                    EntityEvent::Updated,
465                )),
466                ids: to_update.clone(),
467                data: None,
468            });
469        }
470        if !to_delete.is_empty() {
471            event_buffer.push(Event {
472                origin: Origin::DirectAccess(DirectAccessEntity::UserInterface(
473                    EntityEvent::Removed,
474                )),
475                ids: to_delete.clone(),
476                data: None,
477            });
478        }
479
480        Ok(())
481    }
482
483    /// Surgically reconcile one external referrer junction (out-of-scope key -> in-scope values):
484    /// `workspace.user_interface`.
485    fn reconcile_backref_workspace_user_interface(
486        &self,
487        event_buffer: &mut EventBuffer,
488        snap: &HashMapStoreSnapshot,
489        scope_ids: &[EntityId],
490    ) -> Result<(), RepositoryError> {
491        let scope: std::collections::HashSet<EntityId> = scope_ids.iter().copied().collect();
492        if scope.is_empty() {
493            return Ok(());
494        }
495        let store = self.transaction.get_store();
496
497        // External left keys whose ordered list references any scope id, in snapshot or live.
498        let mut left_keys: std::collections::HashSet<EntityId> = std::collections::HashSet::new();
499        for (left, rights) in snap.jn_user_interface_from_workspace_user_interface.iter() {
500            if rights.iter().any(|rid| scope.contains(rid)) {
501                left_keys.insert(*left);
502            }
503        }
504        {
505            let live_jn = read_or_recover(&store.jn_user_interface_from_workspace_user_interface);
506            for (left, rights) in live_jn.iter() {
507                if rights.iter().any(|rid| scope.contains(rid)) {
508                    left_keys.insert(*left);
509                }
510            }
511        }
512
513        for left in left_keys {
514            let snap_list: Vec<EntityId> = snap
515                .jn_user_interface_from_workspace_user_interface
516                .get(&left)
517                .cloned()
518                .unwrap_or_default();
519            let new_list = {
520                let live_jn =
521                    read_or_recover(&store.jn_user_interface_from_workspace_user_interface);
522                let live_list: Vec<EntityId> = live_jn.get(&left).cloned().unwrap_or_default();
523                let reconciled = crate::database::hashmap_store::reconcile_backref_list(
524                    &live_list, &snap_list, &scope,
525                );
526                if reconciled == live_list {
527                    None
528                } else {
529                    Some(reconciled)
530                }
531            };
532            if let Some(reconciled) = new_list {
533                write_or_recover(&store.jn_user_interface_from_workspace_user_interface)
534                    .insert(left, reconciled);
535                event_buffer.push(Event {
536                    origin: Origin::DirectAccess(DirectAccessEntity::Workspace(
537                        EntityEvent::Updated,
538                    )),
539                    ids: vec![left],
540                    data: None,
541                });
542            }
543        }
544        Ok(())
545    }
546}
547
548pub struct UserInterfaceRepositoryRO<'a> {
549    table: Box<dyn UserInterfaceTableRO + 'a>,
550}
551impl<'a> UserInterfaceRepositoryRO<'a> {
552    pub fn new(table: Box<dyn UserInterfaceTableRO + 'a>) -> Self {
553        UserInterfaceRepositoryRO { table }
554    }
555    pub fn get(&self, id: &EntityId) -> Result<Option<UserInterface>, RepositoryError> {
556        self.table.get(id)
557    }
558    pub fn get_multi(
559        &self,
560        ids: &[EntityId],
561    ) -> Result<Vec<Option<UserInterface>>, RepositoryError> {
562        self.table.get_multi(ids)
563    }
564    pub fn get_all(&self) -> Result<Vec<UserInterface>, RepositoryError> {
565        self.table.get_all()
566    }
567}