use std::ops::Deref;
use uuid::Uuid;
use crate::envelope::Envelope;
use crate::error::{self, Error};
use crate::event::{self, Event, Sequence};
use crate::version::DeserializeVersion;
pub trait Aggregate: Default + Send {
type Command;
type Event: event::Event;
type Error: std::error::Error + Send + Sync + 'static;
fn process(&self, command: Self::Command) -> Result<Self::Event, Self::Error>;
fn apply(self, event: &Self::Event) -> Self;
}
pub struct Root<A> {
id: Uuid,
last_sequence: Sequence,
aggregate: A,
}
impl<A> Root<A> {
pub fn with_aggregate(aggregate: A, id: Uuid, last_sequence: Sequence) -> Self {
Self {
id,
last_sequence,
aggregate,
}
}
pub fn id(this: &Self) -> Uuid {
this.id
}
pub fn last_sequence(this: &Self) -> Sequence {
this.last_sequence
}
pub fn into_inner(this: Self) -> A {
this.aggregate
}
}
impl<A: Aggregate> Root<A> {
pub fn new(id: Uuid) -> Self {
Self {
id,
last_sequence: Sequence::new(),
aggregate: A::default(),
}
}
pub fn try_apply<E>(this: Self, envelope: E) -> error::Result<Self>
where
E: Envelope,
A::Event: for<'de> DeserializeVersion<'de>,
{
if envelope.name() != A::Event::name() || envelope.id() != this.id {
return Err(Error::Invalid);
}
let next_sequence = envelope.sequence();
if this.last_sequence >= next_sequence {
Err(Error::Invalid)
} else {
let event = envelope.deserialize::<A::Event>()?;
Ok(Self {
id: this.id,
last_sequence: next_sequence,
aggregate: this.aggregate.apply(&event),
})
}
}
}
impl<A> Deref for Root<A> {
type Target = A;
fn deref(&self) -> &Self::Target {
&self.aggregate
}
}