batpak 0.9.0

Event sourcing with causal graphs and caller-defined gates. Sync API, no async runtime.
Documentation
use crate::event::{EventSourced, ProjectionStateContract, StateExtent, StateExtentCost};
use crate::store::projection::registry::ProjectionRegistry;
use crate::store::StoreError;

pub(super) fn validate_projection_state<T>(
    entity: &str,
    value: Option<&T>,
) -> Result<(), StoreError>
where
    T: EventSourced + 'static,
{
    let projection = ProjectionRegistry::id_for_type::<T>(entity);
    match T::STATE_CONTRACT {
        ProjectionStateContract::Unspecified => {
            Err(StoreError::ProjectionStateContractUnspecified { projection })
        }
        ProjectionStateContract::UnboundedDeclared { .. } => Ok(()),
        declared @ ProjectionStateContract::Bounded {
            max_cardinality, ..
        } => {
            let actual = value.map_or(
                StateExtent::cardinality(0, StateExtentCost::ConstantTime),
                EventSourced::state_extent,
            );
            let Some(cardinality) = actual.cardinality else {
                return Err(StoreError::ProjectionStateExtentUnavailable {
                    projection,
                    declared: Box::new(declared),
                    actual,
                });
            };
            if actual.cost == StateExtentCost::Unavailable {
                return Err(StoreError::ProjectionStateExtentUnavailable {
                    projection,
                    declared: Box::new(declared),
                    actual,
                });
            }
            if cardinality > max_cardinality {
                return Err(StoreError::ProjectionStateBoundExceeded {
                    projection,
                    declared: Box::new(declared),
                    actual,
                });
            }
            Ok(())
        }
    }
}