Skip to main content

common/direct_access/use_cases/
create.rs

1// Generated by Qleany v1.11.0 from common_da_use_cases_create.tera
2
3use super::traits::OwnedWriteUoWFactory;
4use crate::snapshot::EntityTreeSnapshot;
5use crate::types::{EntityId, HasId};
6use crate::undo_redo::UndoRedoCommand;
7use anyhow::{Ok, Result};
8use std::any::Any;
9
10/// Strategy for how the owner relationship is managed on create.
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum OwnerStrategy {
13    /// OneToOne / ManyToOne: new children replace existing children.
14    Replacing,
15    /// OrderedOneToMany / OneToMany / ManyToMany: new children are appended (or inserted at index).
16    Appending,
17}
18
19// ---------------------------------------------------------------------------
20// Non-undoable create (with owner)
21// ---------------------------------------------------------------------------
22
23pub struct CreateUseCase<F: OwnedWriteUoWFactory> {
24    uow_factory: F,
25}
26
27impl<F: OwnedWriteUoWFactory> CreateUseCase<F> {
28    pub fn new(uow_factory: F) -> Self {
29        CreateUseCase { uow_factory }
30    }
31
32    pub fn execute(
33        &mut self,
34        entity: &F::Entity,
35        owner_id: EntityId,
36        index: i32,
37    ) -> Result<F::Entity> {
38        let results = self.execute_multi(std::slice::from_ref(entity), owner_id, index)?;
39        results
40            .into_iter()
41            .next()
42            .ok_or_else(|| anyhow::anyhow!("Create returned empty"))
43    }
44
45    pub fn execute_multi(
46        &mut self,
47        entities: &[F::Entity],
48        owner_id: EntityId,
49        index: i32,
50    ) -> Result<Vec<F::Entity>> {
51        let mut uow = self.uow_factory.create();
52        uow.begin_transaction()?;
53        let created = uow.create_multi(entities, owner_id, index)?;
54        uow.commit()?;
55        Ok(created)
56    }
57}
58
59// ---------------------------------------------------------------------------
60// Undoable create (with owner)
61// ---------------------------------------------------------------------------
62
63pub struct UndoableCreateUseCase<F: OwnedWriteUoWFactory> {
64    uow_factory: F,
65    strategy: OwnerStrategy,
66    created_entities: Vec<F::Entity>,
67    old_tree_snapshot: Option<EntityTreeSnapshot>,
68    // Replacing strategy: the children displaced (cascade-removed) when the owner relationship
69    // is replaced, captured so undo can restore them.
70    existing_child_ids: Vec<EntityId>,
71    displaced_children_snapshot: Option<EntityTreeSnapshot>,
72}
73
74impl<F: OwnedWriteUoWFactory> UndoableCreateUseCase<F> {
75    pub fn new(uow_factory: F, strategy: OwnerStrategy) -> Self {
76        UndoableCreateUseCase {
77            uow_factory,
78            strategy,
79            created_entities: Vec::new(),
80            old_tree_snapshot: None,
81            existing_child_ids: Vec::new(),
82            displaced_children_snapshot: None,
83        }
84    }
85
86    pub fn execute(
87        &mut self,
88        entity: &F::Entity,
89        owner_id: EntityId,
90        index: i32,
91    ) -> Result<F::Entity> {
92        let results = self.execute_multi(std::slice::from_ref(entity), owner_id, index)?;
93        results
94            .into_iter()
95            .next()
96            .ok_or_else(|| anyhow::anyhow!("Create returned empty"))
97    }
98
99    pub fn execute_multi(
100        &mut self,
101        entities: &[F::Entity],
102        owner_id: EntityId,
103        index: i32,
104    ) -> Result<Vec<F::Entity>> {
105        let mut uow = self.uow_factory.create();
106        uow.begin_transaction()?;
107
108        // Replacing displaces the owner's current children (cascade-removed by create); capture
109        // them so undo can restore them. Appending needs no extra capture — undo/redo are surgical.
110        if let OwnerStrategy::Replacing = self.strategy {
111            self.existing_child_ids = uow.get_relationships_from_owner(&owner_id)?;
112            if !self.existing_child_ids.is_empty() {
113                self.displaced_children_snapshot = Some(uow.snapshot(&self.existing_child_ids)?);
114            }
115        }
116
117        let created = uow.create_multi(entities, owner_id, index)?;
118        uow.commit()?;
119
120        self.created_entities = created.clone();
121        Ok(created)
122    }
123}
124
125impl<F: OwnedWriteUoWFactory + Send + 'static> UndoRedoCommand for UndoableCreateUseCase<F>
126where
127    F::Entity: 'static,
128{
129    fn undo(&mut self) -> Result<()> {
130        if !self.created_entities.is_empty() {
131            let ids_to_remove: Vec<EntityId> =
132                self.created_entities.iter().map(|e| e.id()).collect();
133            let mut uow = self.uow_factory.create();
134            uow.begin_transaction()?;
135
136            // Capture the created subtree so redo can re-add it, then remove it. `remove_multi`
137            // surgically drops the created ids from their owner (siblings on other undo stacks
138            // survive).
139            self.old_tree_snapshot = Some(uow.snapshot(&ids_to_remove)?);
140            uow.remove_multi(&ids_to_remove)?;
141
142            // Replacing also cascade-removed the previously-owned children on execute; the scoped
143            // restore re-adds them and surgically re-links them into the owner.
144            if let OwnerStrategy::Replacing = self.strategy
145                && let Some(ref snap) = self.displaced_children_snapshot
146            {
147                uow.restore(snap)?;
148            }
149
150            uow.commit()?;
151        }
152        Ok(())
153    }
154
155    fn redo(&mut self) -> Result<()> {
156        if let Some(ref snapshot) = self.old_tree_snapshot {
157            let mut uow = self.uow_factory.create();
158            uow.begin_transaction()?;
159
160            // Replacing re-displaces the current children (snapshot for the next undo, then remove).
161            if let OwnerStrategy::Replacing = self.strategy
162                && !self.existing_child_ids.is_empty()
163            {
164                self.displaced_children_snapshot = Some(uow.snapshot(&self.existing_child_ids)?);
165                uow.remove_multi(&self.existing_child_ids)?;
166            }
167
168            // Scoped restore re-adds the created subtree and surgically re-links it into the owner.
169            uow.restore(snapshot)?;
170
171            uow.commit()?;
172        }
173        Ok(())
174    }
175
176    fn as_any(&self) -> &dyn Any {
177        self
178    }
179}