text-document-common 1.4.0

Shared entities, database, events, and undo/redo infrastructure for text-document
Documentation
// Generated by Qleany v1.5.0 from common_entity_repository.tera

use std::fmt::Display;

use crate::{
    database::transactions::Transaction,
    direct_access::repository_factory,
    entities::Resource,
    event::{DirectAccessEntity, EntityEvent, Event, EventBuffer, Origin},
    snapshot::EntityTreeSnapshot,
    types::EntityId,
};

use crate::direct_access::document::DocumentRelationshipField;
use crate::error::RepositoryError;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ResourceRelationshipField {}

impl Display for ResourceRelationshipField {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

pub trait ResourceTable {
    fn create(&mut self, entity: &Resource) -> Result<Resource, RepositoryError>;
    fn create_multi(&mut self, entities: &[Resource]) -> Result<Vec<Resource>, RepositoryError>;
    fn get(&self, id: &EntityId) -> Result<Option<Resource>, RepositoryError>;
    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Resource>>, RepositoryError>;
    fn get_all(&self) -> Result<Vec<Resource>, RepositoryError>;
    fn update(&mut self, entity: &Resource) -> Result<Resource, RepositoryError>;
    fn update_multi(&mut self, entities: &[Resource]) -> Result<Vec<Resource>, RepositoryError>;
    fn update_with_relationships(&mut self, entity: &Resource)
    -> Result<Resource, RepositoryError>;
    fn update_with_relationships_multi(
        &mut self,
        entities: &[Resource],
    ) -> Result<Vec<Resource>, RepositoryError>;
    fn remove(&mut self, id: &EntityId) -> Result<(), RepositoryError>;
    fn remove_multi(&mut self, ids: &[EntityId]) -> Result<(), RepositoryError>;
}

pub trait ResourceTableRO {
    fn get(&self, id: &EntityId) -> Result<Option<Resource>, RepositoryError>;
    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Resource>>, RepositoryError>;
    fn get_all(&self) -> Result<Vec<Resource>, RepositoryError>;
}

pub struct ResourceRepository<'a> {
    redb_table: Box<dyn ResourceTable + 'a>,
    transaction: &'a Transaction,
}

impl<'a> ResourceRepository<'a> {
    pub fn new(redb_table: Box<dyn ResourceTable + 'a>, transaction: &'a Transaction) -> Self {
        ResourceRepository {
            redb_table,
            transaction,
        }
    }

    pub fn create_orphan(
        &mut self,
        event_buffer: &mut EventBuffer,
        entity: &Resource,
    ) -> Result<Resource, RepositoryError> {
        let new = self.redb_table.create(entity)?;
        event_buffer.push(Event {
            origin: Origin::DirectAccess(DirectAccessEntity::Resource(EntityEvent::Created)),
            ids: vec![new.id],
            data: None,
        });
        Ok(new)
    }

    pub fn create_orphan_multi(
        &mut self,
        event_buffer: &mut EventBuffer,
        entities: &[Resource],
    ) -> Result<Vec<Resource>, RepositoryError> {
        let new_entities = self.redb_table.create_multi(entities)?;
        event_buffer.push(Event {
            origin: Origin::DirectAccess(DirectAccessEntity::Resource(EntityEvent::Created)),
            ids: new_entities.iter().map(|e| e.id).collect(),
            data: None,
        });
        Ok(new_entities)
    }
    pub fn create(
        &mut self,
        event_buffer: &mut EventBuffer,
        entity: &Resource,
        owner_id: EntityId,
        index: i32,
    ) -> Result<Resource, RepositoryError> {
        let new = self.redb_table.create(entity)?;
        let created_id = new.id;

        let mut relationship_ids = self.get_relationships_from_owner(&owner_id)?;
        // Insert at index
        if index >= 0 && (index as usize) < relationship_ids.len() {
            relationship_ids.insert(index as usize, created_id);
        } else {
            relationship_ids.push(created_id);
        }

        self.set_relationships_in_owner(event_buffer, &owner_id, &relationship_ids)?;
        event_buffer.push(Event {
            origin: Origin::DirectAccess(DirectAccessEntity::Resource(EntityEvent::Created)),
            ids: vec![created_id],
            data: None,
        });
        Ok(new)
    }

    pub fn create_multi(
        &mut self,
        event_buffer: &mut EventBuffer,
        entities: &[Resource],
        owner_id: EntityId,
        index: i32,
    ) -> Result<Vec<Resource>, RepositoryError> {
        let new_entities = self.redb_table.create_multi(entities)?;
        let created_ids: Vec<EntityId> = new_entities.iter().map(|e| e.id).collect();

        let mut relationship_ids = self.get_relationships_from_owner(&owner_id)?;
        if index >= 0 && (index as usize) < relationship_ids.len() {
            for (i, id) in created_ids.iter().enumerate() {
                relationship_ids.insert(index as usize + i, *id);
            }
        } else {
            relationship_ids.extend(created_ids.iter());
        }

        self.set_relationships_in_owner(event_buffer, &owner_id, &relationship_ids)?;
        event_buffer.push(Event {
            origin: Origin::DirectAccess(DirectAccessEntity::Resource(EntityEvent::Created)),
            ids: created_ids,
            data: None,
        });
        Ok(new_entities)
    }

    pub fn get(&self, id: &EntityId) -> Result<Option<Resource>, RepositoryError> {
        self.redb_table.get(id)
    }
    pub fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Resource>>, RepositoryError> {
        self.redb_table.get_multi(ids)
    }
    pub fn get_all(&self) -> Result<Vec<Resource>, RepositoryError> {
        self.redb_table.get_all()
    }

    pub fn update(
        &mut self,
        event_buffer: &mut EventBuffer,
        entity: &Resource,
    ) -> Result<Resource, RepositoryError> {
        let updated = self.redb_table.update(entity)?;
        event_buffer.push(Event {
            origin: Origin::DirectAccess(DirectAccessEntity::Resource(EntityEvent::Updated)),
            ids: vec![updated.id],
            data: None,
        });
        Ok(updated)
    }

    pub fn update_multi(
        &mut self,
        event_buffer: &mut EventBuffer,
        entities: &[Resource],
    ) -> Result<Vec<Resource>, RepositoryError> {
        let updated = self.redb_table.update_multi(entities)?;
        event_buffer.push(Event {
            origin: Origin::DirectAccess(DirectAccessEntity::Resource(EntityEvent::Updated)),
            ids: updated.iter().map(|e| e.id).collect(),
            data: None,
        });
        Ok(updated)
    }

    pub fn update_with_relationships(
        &mut self,
        event_buffer: &mut EventBuffer,
        entity: &Resource,
    ) -> Result<Resource, RepositoryError> {
        let updated = self.redb_table.update_with_relationships(entity)?;
        event_buffer.push(Event {
            origin: Origin::DirectAccess(DirectAccessEntity::Resource(EntityEvent::Updated)),
            ids: vec![updated.id],
            data: None,
        });
        Ok(updated)
    }

    pub fn update_with_relationships_multi(
        &mut self,
        event_buffer: &mut EventBuffer,
        entities: &[Resource],
    ) -> Result<Vec<Resource>, RepositoryError> {
        let updated = self.redb_table.update_with_relationships_multi(entities)?;
        event_buffer.push(Event {
            origin: Origin::DirectAccess(DirectAccessEntity::Resource(EntityEvent::Updated)),
            ids: updated.iter().map(|e| e.id).collect(),
            data: None,
        });
        Ok(updated)
    }

    pub fn remove(
        &mut self,
        event_buffer: &mut EventBuffer,
        id: &EntityId,
    ) -> Result<(), RepositoryError> {
        let _entity = match self.redb_table.get(id)? {
            Some(e) => e,
            None => return Ok(()),
        };
        // get all strong forward relationship fields

        // remove all strong relationships, initiating a cascade remove

        // Before removal, find which owner(s) reference this entity
        let affected_owner_ids: Vec<EntityId> = {
            let owner_repo =
                repository_factory::write::create_document_repository(self.transaction);
            owner_repo
                .get_relationships_from_right_ids(&DocumentRelationshipField::Resources, &[*id])?
                .into_iter()
                .map(|(owner_id, _)| owner_id)
                .collect()
        };
        // Save each owner's current relationship IDs (properly ordered via get_relationships_from_owner)
        let mut owner_rel_before: std::collections::HashMap<EntityId, Vec<EntityId>> =
            std::collections::HashMap::new();
        for owner_id in &affected_owner_ids {
            owner_rel_before.insert(*owner_id, self.get_relationships_from_owner(owner_id)?);
        }

        // remove entity
        self.redb_table.remove(id)?;
        event_buffer.push(Event {
            origin: Origin::DirectAccess(DirectAccessEntity::Resource(EntityEvent::Removed)),
            ids: vec![*id],
            data: None,
        });
        // Update each affected owner's relationship to exclude removed ID (emits Updated event)
        for owner_id in &affected_owner_ids {
            if let Some(rel_ids) = owner_rel_before.get(owner_id) {
                let updated: Vec<EntityId> =
                    rel_ids.iter().copied().filter(|rid| *rid != *id).collect();
                self.set_relationships_in_owner(event_buffer, owner_id, &updated)?;
            }
        }

        Ok(())
    }

    pub fn remove_multi(
        &mut self,
        event_buffer: &mut EventBuffer,
        ids: &[EntityId],
    ) -> Result<(), RepositoryError> {
        let entities = self.redb_table.get_multi(ids)?;
        if entities.is_empty() || entities.iter().all(|e| e.is_none()) {
            return Ok(());
        }

        // get all strong forward relationship fields

        // remove all strong relationships, initiating a cascade remove

        // Before removal, find which owner(s) reference these entities
        let affected_owner_ids: Vec<EntityId> = {
            let owner_repo =
                repository_factory::write::create_document_repository(self.transaction);
            owner_repo
                .get_relationships_from_right_ids(&DocumentRelationshipField::Resources, ids)?
                .into_iter()
                .map(|(owner_id, _)| owner_id)
                .collect()
        };
        // Save each owner's current relationship IDs (properly ordered via get_relationships_from_owner)
        let mut owner_rel_before: std::collections::HashMap<EntityId, Vec<EntityId>> =
            std::collections::HashMap::new();
        for owner_id in &affected_owner_ids {
            owner_rel_before.insert(*owner_id, self.get_relationships_from_owner(owner_id)?);
        }

        self.redb_table.remove_multi(ids)?;
        event_buffer.push(Event {
            origin: Origin::DirectAccess(DirectAccessEntity::Resource(EntityEvent::Removed)),
            ids: ids.into(),
            data: None,
        });
        // Update each affected owner's relationship to exclude removed IDs (emits Updated event)
        {
            let removed_set: std::collections::HashSet<EntityId> = ids.iter().copied().collect();
            for owner_id in &affected_owner_ids {
                if let Some(rel_ids) = owner_rel_before.get(owner_id) {
                    let updated: Vec<EntityId> = rel_ids
                        .iter()
                        .copied()
                        .filter(|rid| !removed_set.contains(rid))
                        .collect();
                    self.set_relationships_in_owner(event_buffer, owner_id, &updated)?;
                }
            }
        }

        Ok(())
    }
    pub fn get_relationships_from_owner(
        &self,
        owner_id: &EntityId,
    ) -> Result<Vec<EntityId>, RepositoryError> {
        let repo = repository_factory::write::create_document_repository(self.transaction);
        repo.get_relationship(owner_id, &DocumentRelationshipField::Resources)
    }

    pub fn set_relationships_in_owner(
        &mut self,
        event_buffer: &mut EventBuffer,
        owner_id: &EntityId,
        ids: &[EntityId],
    ) -> Result<(), RepositoryError> {
        let mut repo = repository_factory::write::create_document_repository(self.transaction);
        repo.set_relationship(
            event_buffer,
            owner_id,
            &DocumentRelationshipField::Resources,
            ids,
        )
    }

    pub fn snapshot(&self, _ids: &[EntityId]) -> Result<EntityTreeSnapshot, RepositoryError> {
        let store_snap = self.transaction.snapshot_store();
        Ok(EntityTreeSnapshot {
            store_snapshot: Some(store_snap),
        })
    }

    pub fn restore(
        &mut self,
        event_buffer: &mut EventBuffer,
        snap: &EntityTreeSnapshot,
    ) -> Result<(), RepositoryError> {
        let store_snap = snap
            .store_snapshot
            .as_ref()
            .ok_or_else(|| RepositoryError::Serialization("missing store snapshot".into()))?;
        self.transaction.restore_store(store_snap);

        let store = self.transaction.get_store();

        // Resource: Created only (leaf, no strong children)
        let res_ids: Vec<_> = store.resources.read().unwrap().keys().copied().collect();
        if !res_ids.is_empty() {
            event_buffer.push(Event {
                origin: Origin::DirectAccess(DirectAccessEntity::Resource(EntityEvent::Created)),
                ids: res_ids,
                data: None,
            });
        }

        Ok(())
    }
}

pub struct ResourceRepositoryRO<'a> {
    redb_table: Box<dyn ResourceTableRO + 'a>,
}
impl<'a> ResourceRepositoryRO<'a> {
    pub fn new(redb_table: Box<dyn ResourceTableRO + 'a>) -> Self {
        ResourceRepositoryRO { redb_table }
    }
    pub fn get(&self, id: &EntityId) -> Result<Option<Resource>, RepositoryError> {
        self.redb_table.get(id)
    }
    pub fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Resource>>, RepositoryError> {
        self.redb_table.get_multi(ids)
    }
    pub fn get_all(&self) -> Result<Vec<Resource>, RepositoryError> {
        self.redb_table.get_all()
    }
}