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. List has no forward relationships;
// backward cleanup on remove iterates Blocks (clear `block.list`) and
// Documents (strip from `doc.lists`) directly.

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

use super::list_repository::ListTable;
use super::list_repository::ListTableRO;

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

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

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

    fn create_multi(&mut self, entities: &[List]) -> Result<Vec<List>, RepositoryError> {
        let mut created = Vec::with_capacity(entities.len());
        let mut map = self.store.lists.write().unwrap();
        for entity in entities {
            let new_entity = if entity.id == EntityId::default() {
                let id = self.store.next_id("list");
                List {
                    id,
                    ..entity.clone()
                }
            } else {
                if map.contains_key(&entity.id) {
                    return Err(RepositoryError::DuplicateId {
                        entity: "List",
                        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<List>, RepositoryError> {
        Ok(self.store.lists.read().unwrap().get(id).cloned())
    }

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

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

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

    fn update_multi(&mut self, entities: &[List]) -> Result<Vec<List>, RepositoryError> {
        let mut map = self.store.lists.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: &List) -> Result<List, RepositoryError> {
        self.update(entity)
    }

    fn update_with_relationships_multi(
        &mut self,
        entities: &[List],
    ) -> Result<Vec<List>, 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.lists.write().unwrap();
            for id in ids {
                map.remove(id);
            }
        }

        // Backward cleanup: Blocks whose `list` pointed at a removed List.
        {
            let mut block_map = self.store.blocks.write().unwrap();
            let updates: Vec<(EntityId, crate::entities::Block)> = block_map
                .iter()
                .filter_map(|(bid, b)| {
                    b.list.filter(|l| removed.contains(l)).map(|_| {
                        let mut updated = b.clone();
                        updated.list = None;
                        (*bid, updated)
                    })
                })
                .collect();
            for (bid, b) in updates {
                block_map.insert(bid, b);
            }
        }

        // Backward cleanup: Documents whose `lists` Vec listed a removed List.
        {
            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.lists.iter().any(|lid| removed.contains(lid)) {
                        let mut updated = d.clone();
                        updated.lists.retain(|lid| !removed.contains(lid));
                        Some((*did, updated))
                    } else {
                        None
                    }
                })
                .collect();
            for (did, d) in updates {
                doc_map.insert(did, d);
            }
        }

        Ok(())
    }
}

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

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

impl<'a> ListTableRO for ListHashMapTableRO<'a> {
    fn get(&self, id: &EntityId) -> Result<Option<List>, RepositoryError> {
        Ok(self.store.lists.read().unwrap().get(id).cloned())
    }

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

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