text-document-common 1.5.4

Shared entities, database, events, and undo/redo infrastructure for text-document
Documentation
// Generated by Qleany v1.7.3 from common_entity_table.tera
// Phase 2 step 6: junction-free. Resource has no forward
// relationships; backward cleanup on remove iterates Documents and
// strips removed ids from `doc.resources`.

use crate::database::Store;
use crate::entities::Resource;
use crate::error::RepositoryError;
use crate::types::EntityId;

use super::resource_repository::ResourceTable;
use super::resource_repository::ResourceTableRO;

pub struct ResourceHashMapTable<'a> {
    store: &'a Store,
}

impl<'a> ResourceHashMapTable<'a> {
    pub fn new(store: &'a Store) -> Self {
        Self { store }
    }
}

impl<'a> ResourceTable for ResourceHashMapTable<'a> {
    fn create(&mut self, entity: &Resource) -> Result<Resource, RepositoryError> {
        self.create_multi(std::slice::from_ref(entity))
            .map(|v| v.into_iter().next().unwrap())
    }

    fn create_multi(&mut self, entities: &[Resource]) -> Result<Vec<Resource>, RepositoryError> {
        let mut created = Vec::with_capacity(entities.len());
        let mut map = self.store.resources.write().unwrap();
        for entity in entities {
            let new_entity = if entity.id == EntityId::default() {
                let id = self.store.next_id("resource");
                Resource {
                    id,
                    ..entity.clone()
                }
            } else {
                if map.contains_key(&entity.id) {
                    return Err(RepositoryError::DuplicateId {
                        entity: "Resource",
                        id: entity.id,
                    });
                }
                entity.clone()
            };
            map.insert(new_entity.id, new_entity.clone());
            created.push(new_entity);
        }
        Ok(created)
    }

    fn get(&self, id: &EntityId) -> Result<Option<Resource>, RepositoryError> {
        Ok(self.store.resources.read().unwrap().get(id).cloned())
    }

    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Resource>>, RepositoryError> {
        let map = self.store.resources.read().unwrap();
        Ok(ids.iter().map(|id| map.get(id).cloned()).collect())
    }

    fn get_all(&self) -> Result<Vec<Resource>, RepositoryError> {
        Ok(self
            .store
            .resources
            .read()
            .unwrap()
            .values()
            .cloned()
            .collect())
    }

    fn update(&mut self, entity: &Resource) -> Result<Resource, RepositoryError> {
        self.update_multi(std::slice::from_ref(entity))
            .map(|v| v.into_iter().next().unwrap())
    }

    fn update_multi(&mut self, entities: &[Resource]) -> Result<Vec<Resource>, RepositoryError> {
        let mut map = self.store.resources.write().unwrap();
        let mut result = Vec::with_capacity(entities.len());
        for entity in entities {
            map.insert(entity.id, entity.clone());
            result.push(entity.clone());
        }
        Ok(result)
    }

    fn update_with_relationships(
        &mut self,
        entity: &Resource,
    ) -> Result<Resource, RepositoryError> {
        self.update(entity)
    }

    fn update_with_relationships_multi(
        &mut self,
        entities: &[Resource],
    ) -> Result<Vec<Resource>, RepositoryError> {
        self.update_multi(entities)
    }

    fn remove(&mut self, id: &EntityId) -> Result<(), RepositoryError> {
        self.remove_multi(std::slice::from_ref(id))
    }

    fn remove_multi(&mut self, ids: &[EntityId]) -> Result<(), RepositoryError> {
        let removed: std::collections::HashSet<EntityId> = ids.iter().copied().collect();

        {
            let mut map = self.store.resources.write().unwrap();
            for id in ids {
                map.remove(id);
            }
        }

        // Backward cleanup: Documents whose `resources` Vec listed a
        // removed Resource.
        {
            let mut doc_map = self.store.documents.write().unwrap();
            let updates: Vec<(EntityId, crate::entities::Document)> = doc_map
                .iter()
                .filter_map(|(did, d)| {
                    if d.resources.iter().any(|rid| removed.contains(rid)) {
                        let mut updated = d.clone();
                        updated.resources.retain(|rid| !removed.contains(rid));
                        Some((*did, updated))
                    } else {
                        None
                    }
                })
                .collect();
            for (did, d) in updates {
                doc_map.insert(did, d);
            }
        }

        Ok(())
    }
}

pub struct ResourceHashMapTableRO<'a> {
    store: &'a Store,
}

impl<'a> ResourceHashMapTableRO<'a> {
    pub fn new(store: &'a Store) -> Self {
        Self { store }
    }
}

impl<'a> ResourceTableRO for ResourceHashMapTableRO<'a> {
    fn get(&self, id: &EntityId) -> Result<Option<Resource>, RepositoryError> {
        Ok(self.store.resources.read().unwrap().get(id).cloned())
    }

    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Resource>>, RepositoryError> {
        let map = self.store.resources.read().unwrap();
        Ok(ids.iter().map(|id| map.get(id).cloned()).collect())
    }

    fn get_all(&self) -> Result<Vec<Resource>, RepositoryError> {
        Ok(self
            .store
            .resources
            .read()
            .unwrap()
            .values()
            .cloned()
            .collect())
    }
}