qleany-common 2.0.0

Shared entities, database, events, and undo/redo infrastructure for Qleany
Documentation
// Generated by Qleany v1.11.0 from common_da_use_cases_create.tera

use super::traits::OwnedWriteUoWFactory;
use crate::snapshot::EntityTreeSnapshot;
use crate::types::{EntityId, HasId};
use crate::undo_redo::UndoRedoCommand;
use anyhow::{Ok, Result};
use std::any::Any;

/// Strategy for how the owner relationship is managed on create.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum OwnerStrategy {
    /// OneToOne / ManyToOne: new children replace existing children.
    Replacing,
    /// OrderedOneToMany / OneToMany / ManyToMany: new children are appended (or inserted at index).
    Appending,
}

// ---------------------------------------------------------------------------
// Non-undoable create (with owner)
// ---------------------------------------------------------------------------

pub struct CreateUseCase<F: OwnedWriteUoWFactory> {
    uow_factory: F,
}

impl<F: OwnedWriteUoWFactory> CreateUseCase<F> {
    pub fn new(uow_factory: F) -> Self {
        CreateUseCase { uow_factory }
    }

    pub fn execute(
        &mut self,
        entity: &F::Entity,
        owner_id: EntityId,
        index: i32,
    ) -> Result<F::Entity> {
        let results = self.execute_multi(std::slice::from_ref(entity), owner_id, index)?;
        results
            .into_iter()
            .next()
            .ok_or_else(|| anyhow::anyhow!("Create returned empty"))
    }

    pub fn execute_multi(
        &mut self,
        entities: &[F::Entity],
        owner_id: EntityId,
        index: i32,
    ) -> Result<Vec<F::Entity>> {
        let mut uow = self.uow_factory.create();
        uow.begin_transaction()?;
        let created = uow.create_multi(entities, owner_id, index)?;
        uow.commit()?;
        Ok(created)
    }
}

// ---------------------------------------------------------------------------
// Undoable create (with owner)
// ---------------------------------------------------------------------------

pub struct UndoableCreateUseCase<F: OwnedWriteUoWFactory> {
    uow_factory: F,
    strategy: OwnerStrategy,
    created_entities: Vec<F::Entity>,
    old_tree_snapshot: Option<EntityTreeSnapshot>,
    // Replacing strategy: the children displaced (cascade-removed) when the owner relationship
    // is replaced, captured so undo can restore them.
    existing_child_ids: Vec<EntityId>,
    displaced_children_snapshot: Option<EntityTreeSnapshot>,
}

impl<F: OwnedWriteUoWFactory> UndoableCreateUseCase<F> {
    pub fn new(uow_factory: F, strategy: OwnerStrategy) -> Self {
        UndoableCreateUseCase {
            uow_factory,
            strategy,
            created_entities: Vec::new(),
            old_tree_snapshot: None,
            existing_child_ids: Vec::new(),
            displaced_children_snapshot: None,
        }
    }

    pub fn execute(
        &mut self,
        entity: &F::Entity,
        owner_id: EntityId,
        index: i32,
    ) -> Result<F::Entity> {
        let results = self.execute_multi(std::slice::from_ref(entity), owner_id, index)?;
        results
            .into_iter()
            .next()
            .ok_or_else(|| anyhow::anyhow!("Create returned empty"))
    }

    pub fn execute_multi(
        &mut self,
        entities: &[F::Entity],
        owner_id: EntityId,
        index: i32,
    ) -> Result<Vec<F::Entity>> {
        let mut uow = self.uow_factory.create();
        uow.begin_transaction()?;

        // Replacing displaces the owner's current children (cascade-removed by create); capture
        // them so undo can restore them. Appending needs no extra capture — undo/redo are surgical.
        if let OwnerStrategy::Replacing = self.strategy {
            self.existing_child_ids = uow.get_relationships_from_owner(&owner_id)?;
            if !self.existing_child_ids.is_empty() {
                self.displaced_children_snapshot = Some(uow.snapshot(&self.existing_child_ids)?);
            }
        }

        let created = uow.create_multi(entities, owner_id, index)?;
        uow.commit()?;

        self.created_entities = created.clone();
        Ok(created)
    }
}

impl<F: OwnedWriteUoWFactory + Send + 'static> UndoRedoCommand for UndoableCreateUseCase<F>
where
    F::Entity: 'static,
{
    fn undo(&mut self) -> Result<()> {
        if !self.created_entities.is_empty() {
            let ids_to_remove: Vec<EntityId> =
                self.created_entities.iter().map(|e| e.id()).collect();
            let mut uow = self.uow_factory.create();
            uow.begin_transaction()?;

            // Capture the created subtree so redo can re-add it, then remove it. `remove_multi`
            // surgically drops the created ids from their owner (siblings on other undo stacks
            // survive).
            self.old_tree_snapshot = Some(uow.snapshot(&ids_to_remove)?);
            uow.remove_multi(&ids_to_remove)?;

            // Replacing also cascade-removed the previously-owned children on execute; the scoped
            // restore re-adds them and surgically re-links them into the owner.
            if let OwnerStrategy::Replacing = self.strategy
                && let Some(ref snap) = self.displaced_children_snapshot
            {
                uow.restore(snap)?;
            }

            uow.commit()?;
        }
        Ok(())
    }

    fn redo(&mut self) -> Result<()> {
        if let Some(ref snapshot) = self.old_tree_snapshot {
            let mut uow = self.uow_factory.create();
            uow.begin_transaction()?;

            // Replacing re-displaces the current children (snapshot for the next undo, then remove).
            if let OwnerStrategy::Replacing = self.strategy
                && !self.existing_child_ids.is_empty()
            {
                self.displaced_children_snapshot = Some(uow.snapshot(&self.existing_child_ids)?);
                uow.remove_multi(&self.existing_child_ids)?;
            }

            // Scoped restore re-adds the created subtree and surgically re-links it into the owner.
            uow.restore(snapshot)?;

            uow.commit()?;
        }
        Ok(())
    }

    fn as_any(&self) -> &dyn Any {
        self
    }
}