use std::fmt;
use crate::entity::{upcast_events, Entity, EventRecord, EventUpcaster};
use crate::repository::RepositoryError;
pub trait Aggregate: Sized + Default {
type ReplayError: fmt::Display;
fn aggregate_type() -> &'static str {
std::any::type_name::<Self>()
}
fn new_empty() -> Self {
Self::default()
}
fn entity(&self) -> &Entity;
fn entity_mut(&mut self) -> &mut Entity;
fn replay_event(&mut self, event: &EventRecord) -> Result<(), Self::ReplayError>;
fn upcasters() -> &'static [EventUpcaster] {
&[]
}
}
#[macro_export]
macro_rules! impl_aggregate {
($ty:ty, $entity:ident, $replay:ident) => {
$crate::impl_aggregate!($ty, $entity, $replay, String);
};
($ty:ty, $entity:ident, $replay:ident, aggregate_type = $aggregate_type:literal) => {
$crate::impl_aggregate!(
$ty,
$entity,
$replay,
String,
aggregate_type = $aggregate_type
);
};
($ty:ty, $entity:ident, $replay:ident, $err:ty) => {
impl $crate::Aggregate for $ty {
type ReplayError = $err;
fn entity(&self) -> &$crate::Entity {
&self.$entity
}
fn entity_mut(&mut self) -> &mut $crate::Entity {
&mut self.$entity
}
fn replay_event(
&mut self,
event: &$crate::EventRecord,
) -> Result<(), Self::ReplayError> {
Self::$replay(self, event)
}
}
};
($ty:ty, $entity:ident, $replay:ident, $err:ty, aggregate_type = $aggregate_type:literal) => {
impl $crate::Aggregate for $ty {
type ReplayError = $err;
fn aggregate_type() -> &'static str {
$aggregate_type
}
fn entity(&self) -> &$crate::Entity {
&self.$entity
}
fn entity_mut(&mut self) -> &mut $crate::Entity {
&mut self.$entity
}
fn replay_event(
&mut self,
event: &$crate::EventRecord,
) -> Result<(), Self::ReplayError> {
Self::$replay(self, event)
}
}
};
}
pub fn hydrate<A: Aggregate>(entity: Entity) -> Result<A, RepositoryError> {
let mut agg = A::new_empty();
*agg.entity_mut() = entity;
let history = agg.entity_mut().take_events();
let upcasters = A::upcasters();
let events = if upcasters.is_empty() {
replay_into(&mut agg, &history)?;
history
} else {
let upcasted = upcast_events(history.clone(), upcasters)
.map_err(|err| RepositoryError::Replay(err.to_string()))?;
replay_into(&mut agg, &upcasted)?;
history
};
agg.entity_mut().load_from_history(events);
Ok(agg)
}
fn replay_into<A: Aggregate>(agg: &mut A, events: &[EventRecord]) -> Result<(), RepositoryError> {
agg.entity_mut().set_replaying(true);
for event in events {
if let Err(err) = agg.replay_event(event) {
agg.entity_mut().set_replaying(false);
return Err(RepositoryError::Replay(err.to_string()));
}
}
agg.entity_mut().set_replaying(false);
Ok(())
}