text-document-common 1.4.0

Shared entities, database, events, and undo/redo infrastructure for text-document
Documentation
// Generated by Qleany v1.5.1 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>,
    owner_id: Option<EntityId>,
    index: i32,
    old_tree_snapshot: Option<EntityTreeSnapshot>,
    // Replacing strategy fields
    existing_child_ids: Vec<EntityId>,
    displaced_children_snapshot: Option<EntityTreeSnapshot>,
    // Appending strategy fields
    previous_owner_relationship_ids: Option<Vec<EntityId>>,
}

impl<F: OwnedWriteUoWFactory> UndoableCreateUseCase<F> {
    pub fn new(uow_factory: F, strategy: OwnerStrategy) -> Self {
        UndoableCreateUseCase {
            uow_factory,
            strategy,
            created_entities: Vec::new(),
            owner_id: None,
            index: -1,
            old_tree_snapshot: None,
            existing_child_ids: Vec::new(),
            displaced_children_snapshot: None,
            previous_owner_relationship_ids: 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()?;

        self.owner_id = Some(owner_id);
        self.index = index;

        match self.strategy {
            OwnerStrategy::Replacing => {
                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)?);
                }
            }
            OwnerStrategy::Appending => {
                self.previous_owner_relationship_ids =
                    Some(uow.get_relationships_from_owner(&owner_id)?);
            }
        }

        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 owner_id = self.owner_id.expect("owner_id not set");
            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()?;

            self.old_tree_snapshot = Some(uow.snapshot(&ids_to_remove)?);
            uow.remove_multi(&ids_to_remove)?;

            match self.strategy {
                OwnerStrategy::Replacing => {
                    if let Some(ref snap) = self.displaced_children_snapshot {
                        uow.restore(snap)?;
                        uow.set_relationships_in_owner(&owner_id, &self.existing_child_ids)?;
                    } else {
                        uow.set_relationships_in_owner(&owner_id, &[])?;
                    }
                }
                OwnerStrategy::Appending => {
                    if let Some(ref prev_ids) = self.previous_owner_relationship_ids {
                        uow.set_relationships_in_owner(&owner_id, prev_ids)?;
                    }
                }
            }

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

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

            match self.strategy {
                OwnerStrategy::Replacing => {
                    if !self.existing_child_ids.is_empty() {
                        self.displaced_children_snapshot =
                            Some(uow.snapshot(&self.existing_child_ids)?);
                        uow.remove_multi(&self.existing_child_ids)?;
                    }
                    uow.restore(snapshot)?;
                    let created_ids: Vec<EntityId> =
                        self.created_entities.iter().map(|e| e.id()).collect();
                    uow.set_relationships_in_owner(&owner_id, &created_ids)?;
                }
                OwnerStrategy::Appending => {
                    uow.restore(snapshot)?;
                    if let Some(ref prev_ids) = self.previous_owner_relationship_ids {
                        let mut redo_junction = prev_ids.clone();
                        let created_ids: Vec<EntityId> =
                            self.created_entities.iter().map(|e| e.id()).collect();
                        if self.index >= 0 && (self.index as usize) < redo_junction.len() {
                            for (i, id) in created_ids.iter().enumerate() {
                                redo_junction.insert(self.index as usize + i, *id);
                            }
                        } else {
                            redo_junction.extend(created_ids.iter());
                        }
                        uow.set_relationships_in_owner(&owner_id, &redo_junction)?;
                    }
                }
            }

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

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