qleany-common 2.0.1

Shared entities, database, events, and undo/redo infrastructure for Qleany
Documentation
// Generated by Qleany v2.0.0 from common_entity_table.tera

// ═══════════════════════════════════════════════════════════════════════
// Entity WITH forward relationships — explicit struct implementation
// ═══════════════════════════════════════════════════════════════════════
use crate::database::hashmap_store::{
    HashMapJunction, HashMapStore, delete_from_backward_junction, junction_get, junction_remove,
    junction_set, read_or_recover, write_or_recover,
};
use crate::entities::Dto;
use crate::error::RepositoryError;
use crate::types::EntityId;
use crate::{impl_relationship_methods, impl_write_relationship_methods};
use std::sync::RwLock;

use super::dto_repository::DtoRelationshipField;
use super::dto_repository::DtoTable;
use super::dto_repository::DtoTableRO;

pub struct DtoHashMapTable<'a> {
    store: &'a HashMapStore,
}

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

    fn resolve_junction(&self, field: &DtoRelationshipField) -> &RwLock<HashMapJunction> {
        match field {
            DtoRelationshipField::Fields => &self.store.jn_dto_field_from_dto_fields,
        }
    }

    fn relationship_source_exists(&self, id: &EntityId) -> bool {
        read_or_recover(&self.store.dtos).contains_key(id)
    }

    fn relationship_is_ordered(&self, field: &DtoRelationshipField) -> bool {
        match field {
            DtoRelationshipField::Fields => true,
        }
    }

    /// Validate a batch of row replacements for one relationship before any is
    /// written. Costs O(targets in `rows`): exclusivity is checked against the
    /// junction's reverse index, never by walking the relationship's other rows.
    fn validate_relationship_updates(
        &self,
        field: &DtoRelationshipField,
        rows: &[(EntityId, Vec<EntityId>)],
        _creating_ids: &[EntityId],
    ) -> Result<(), RepositoryError> {
        let scalar = match field {
            DtoRelationshipField::Fields => {
                let targets = read_or_recover(&self.store.dto_fields);
                let missing: Vec<_> = rows
                    .iter()
                    .flat_map(|(_, ids)| ids)
                    .copied()
                    .filter(|id| !targets.contains_key(id))
                    .collect();
                if !missing.is_empty() {
                    return Err(RepositoryError::MissingRelationshipTarget {
                        operation: "relationship update",
                        ids: missing,
                    });
                }
                false
            }
        };
        crate::database::hashmap_store::validate_junction_updates(
            self.resolve_junction(field),
            rows,
            scalar,
        )
    }

    fn hydrate(&self, entity: &mut Dto) {
        entity.fields = junction_get(&self.store.jn_dto_field_from_dto_fields, &entity.id);
    }
}

impl<'a> DtoTable for DtoHashMapTable<'a> {
    fn create(&mut self, entity: &Dto) -> Result<Dto, RepositoryError> {
        self.create_multi(std::slice::from_ref(entity))
            .and_then(|v| {
                // `create_multi` is contracted to return one row per input, so this
                // is unreachable — but it used to be `.unwrap()`, and a store that
                // ever broke that contract would take the whole process down rather
                // than fail the one call that noticed.
                v.into_iter().next().ok_or_else(|| {
                    RepositoryError::Other(anyhow::anyhow!(
                        "create: the store returned no row for the entity it was given"
                    ))
                })
            })
    }

    fn create_multi(&mut self, entities: &[Dto]) -> Result<Vec<Dto>, RepositoryError> {
        // Assign identities first, then validate the whole batch without holding
        // the source table lock (self-referential relationships read that table).
        let entities: Vec<_> = entities
            .iter()
            .map(|entity| {
                let mut entity = entity.clone();
                if entity.id == EntityId::default() {
                    entity.id = self.store.next_id("dto");
                }
                entity
            })
            .collect();

        self.validate_relationship_updates(
            &DtoRelationshipField::Fields,
            &entities
                .iter()
                .map(|e| (e.id, e.fields.clone()))
                .collect::<Vec<_>>(),
            &entities.iter().map(|e| e.id).collect::<Vec<_>>(),
        )?;
        let mut created = Vec::with_capacity(entities.len());
        let mut dto_map = write_or_recover(&self.store.dtos);

        for entity in &entities {
            let new_entity = if entity.id == EntityId::default() {
                let id = self.store.next_id("dto");
                Dto {
                    id,
                    ..entity.clone()
                }
            } else {
                if dto_map.contains_key(&entity.id) {
                    return Err(RepositoryError::DuplicateId {
                        entity: "Dto",
                        id: entity.id,
                    });
                }
                entity.clone()
            };

            dto_map.insert(new_entity.id, new_entity.clone());

            junction_set(
                &self.store.jn_dto_field_from_dto_fields,
                new_entity.id,
                new_entity.fields.clone(),
            );

            created.push(new_entity);
        }
        Ok(created)
    }

    fn get(&self, id: &EntityId) -> Result<Option<Dto>, RepositoryError> {
        let dto_map = read_or_recover(&self.store.dtos);
        match dto_map.get(id) {
            Some(entity) => {
                let mut e = entity.clone();
                drop(dto_map);
                self.hydrate(&mut e);
                Ok(Some(e))
            }
            None => Ok(None),
        }
    }

    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Dto>>, RepositoryError> {
        let mut result = Vec::with_capacity(ids.len());
        for id in ids {
            result.push(self.get(id)?);
        }
        Ok(result)
    }

    fn get_all(&self) -> Result<Vec<Dto>, RepositoryError> {
        let dto_map = read_or_recover(&self.store.dtos);
        let entries: Vec<Dto> = dto_map.values().cloned().collect();
        drop(dto_map);
        let mut result = Vec::with_capacity(entries.len());
        for mut entity in entries {
            self.hydrate(&mut entity);
            result.push(entity);
        }
        Ok(result)
    }

    fn update(&mut self, entity: &Dto) -> Result<Dto, RepositoryError> {
        self.update_multi(std::slice::from_ref(entity))
            .and_then(|v| {
                // `update_multi` is contracted to return one row per input, so this
                // is unreachable — but it used to be `.unwrap()`, and a store that
                // ever broke that contract would take the whole process down rather
                // than fail the one call that noticed.
                v.into_iter().next().ok_or_else(|| {
                    RepositoryError::Other(anyhow::anyhow!(
                        "update: the store returned no row for the entity it was given"
                    ))
                })
            })
    }

    // Scalar-only update: writes entity data but does NOT touch junction tables.
    fn update_multi(&mut self, entities: &[Dto]) -> Result<Vec<Dto>, RepositoryError> {
        let mut dto_map = write_or_recover(&self.store.dtos);
        for entity in entities {
            dto_map.insert(entity.id, entity.clone());
        }
        drop(dto_map);
        let ids: Vec<EntityId> = entities.iter().map(|e| e.id).collect();
        let result = self.get_multi(&ids)?;
        Ok(result.into_iter().flatten().collect())
    }

    fn update_with_relationships(&mut self, entity: &Dto) -> Result<Dto, RepositoryError> {
        self.update_with_relationships_multi(std::slice::from_ref(entity))
            .and_then(|v| {
                // `update_with_relationships_multi` is contracted to return one row per input, so this
                // is unreachable — but it used to be `.unwrap()`, and a store that
                // ever broke that contract would take the whole process down rather
                // than fail the one call that noticed.
                v.into_iter().next().ok_or_else(|| {
                    RepositoryError::Other(anyhow::anyhow!(
                        "update_with_relationships: the store returned no row for the entity it was given"
                    ))
                })
            })
    }

    fn update_with_relationships_multi(
        &mut self,
        entities: &[Dto],
    ) -> Result<Vec<Dto>, RepositoryError> {
        self.validate_relationship_updates(
            &DtoRelationshipField::Fields,
            &entities
                .iter()
                .map(|e| (e.id, e.fields.clone()))
                .collect::<Vec<_>>(),
            &[],
        )?;
        let mut dto_map = write_or_recover(&self.store.dtos);
        for entity in entities {
            dto_map.insert(entity.id, entity.clone());

            junction_set(
                &self.store.jn_dto_field_from_dto_fields,
                entity.id,
                entity.fields.clone(),
            );
        }
        drop(dto_map);
        let ids: Vec<EntityId> = entities.iter().map(|e| e.id).collect();
        let result = self.get_multi(&ids)?;
        Ok(result.into_iter().flatten().collect())
    }

    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 mut dto_map = write_or_recover(&self.store.dtos);
        for id in ids {
            dto_map.remove(id);

            // Remove forward junction entries

            junction_remove(&self.store.jn_dto_field_from_dto_fields, id);

            // Clean up backward references (uses the owning entity's forward junction)

            delete_from_backward_junction(&self.store.jn_dto_from_use_case_dto_in, id);
            delete_from_backward_junction(&self.store.jn_dto_from_use_case_dto_out, id);
        }
        Ok(())
    }

    impl_write_relationship_methods!(DtoHashMapTable<'a>, DtoRelationshipField);
}

pub struct DtoHashMapTableRO<'a> {
    store: &'a HashMapStore,
}

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

    fn resolve_junction(&self, field: &DtoRelationshipField) -> &RwLock<HashMapJunction> {
        match field {
            DtoRelationshipField::Fields => &self.store.jn_dto_field_from_dto_fields,
        }
    }

    fn hydrate(&self, entity: &mut Dto) {
        entity.fields = junction_get(&self.store.jn_dto_field_from_dto_fields, &entity.id);
    }
}

impl<'a> DtoTableRO for DtoHashMapTableRO<'a> {
    fn get(&self, id: &EntityId) -> Result<Option<Dto>, RepositoryError> {
        let dto_map = read_or_recover(&self.store.dtos);
        match dto_map.get(id) {
            Some(entity) => {
                let mut e = entity.clone();
                drop(dto_map);
                self.hydrate(&mut e);
                Ok(Some(e))
            }
            None => Ok(None),
        }
    }

    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Dto>>, RepositoryError> {
        let mut result = Vec::with_capacity(ids.len());
        for id in ids {
            result.push(self.get(id)?);
        }
        Ok(result)
    }

    fn get_all(&self) -> Result<Vec<Dto>, RepositoryError> {
        let dto_map = read_or_recover(&self.store.dtos);
        let entries: Vec<Dto> = dto_map.values().cloned().collect();
        drop(dto_map);
        let mut result = Vec::with_capacity(entries.len());
        for mut entity in entries {
            self.hydrate(&mut entity);
            result.push(entity);
        }
        Ok(result)
    }

    impl_relationship_methods!(DtoHashMapTableRO<'a>, DtoRelationshipField);
}