use std::any;
use serde_json::Value;
use crate::{Event, Failure, Permanent, UpcastEvent, Upcasters};
#[cfg(test)]
mod tests;
pub trait Events: Sized {
const EVENT_TYPES: &'static [&'static str];
fn schemas() -> Vec<EventSchema>;
fn event_type(&self) -> &'static str;
fn to_data(&self) -> Result<Value, serde_json::Error>;
fn from_data(event_type: &str, data: Value) -> Option<Result<Self, serde_json::Error>>;
#[must_use]
fn has_rule(event_type: &str) -> bool {
Self::EVENT_TYPES.contains(&event_type)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct EventSchema {
event_type: &'static str,
schema: Value,
}
impl EventSchema {
#[must_use]
pub fn of<E: Event>() -> Self {
Self {
event_type: E::EVENT_TYPE,
schema: E::schema(),
}
}
#[must_use]
pub fn event_type(&self) -> &'static str {
self.event_type
}
#[must_use]
pub fn schema(&self) -> &Value {
&self.schema
}
}
pub trait State: Default + Clone {
type Event: Events;
#[must_use]
fn evolve(self, event: &Self::Event) -> Self;
#[must_use]
fn upcasters() -> Option<&'static Upcasters> {
None
}
#[must_use]
fn from_latest() -> Option<&'static str> {
None
}
}
fn from_latest_of<S: State>() -> Option<&'static str> {
let event_type = S::from_latest()?;
assert!(
S::Event::has_rule(event_type),
"architecturekit: event type {event_type:?} has no rule on the state {}, so it can not start from it",
any::type_name::<S>(),
);
Some(event_type)
}
fn start_of<T>(
history: &[T],
event_type_of: impl Fn(&T) -> &str,
from_latest: Option<&str>,
) -> usize {
from_latest
.and_then(|from_latest| {
history
.iter()
.rposition(|event| event_type_of(event) == from_latest)
})
.unwrap_or(0)
}
pub fn replay<S: State>(history: impl IntoIterator<Item = S::Event>) -> Result<S, Permanent> {
let history: Vec<S::Event> = history.into_iter().collect();
let start = start_of(&history, |event| event.event_type(), from_latest_of::<S>());
history[start..]
.iter()
.try_fold(S::default(), |current, event| evolve_by(current, event))
}
pub fn replay_stored<S: State>(
history: impl IntoIterator<Item = UpcastEvent>,
) -> Result<S, Permanent> {
let history: Vec<UpcastEvent> = history.into_iter().collect();
let start = start_of(&history, UpcastEvent::event_type, from_latest_of::<S>());
history
.into_iter()
.skip(start)
.try_fold(S::default(), |current, stored| {
evolve_by_stored(current, stored)
})
}
pub fn step<S: State>(current: &S, event: &S::Event) -> Result<S, Permanent> {
evolve_by(current.clone(), event)
}
pub fn step_stored<S: State>(current: &S, stored: UpcastEvent) -> Result<S, Permanent> {
evolve_by_stored(current.clone(), stored)
}
fn evolve_by<S: State>(current: S, event: &S::Event) -> Result<S, Permanent> {
let event_type = event.event_type();
let data = event.to_data().map_err(|cause| {
Permanent::Other(Failure::new(
format!("encoding {event_type:?}"),
Some(Box::new(cause)),
))
})?;
let decoded = decode::<S::Event>(event_type, data)?;
Ok(current.evolve(&decoded))
}
pub(crate) fn evolve_by_stored<S: State>(current: S, stored: UpcastEvent) -> Result<S, Permanent> {
let upcasted = match S::upcasters() {
Some(upcasters) => upcasters.apply(stored)?,
None => vec![stored],
};
upcasted.into_iter().try_fold(current, |current, event| {
let event_type = event.event_type().to_owned();
let decoded = decode::<S::Event>(&event_type, event.into_data())?;
Ok(current.evolve(&decoded))
})
}
fn decode<E: Events>(event_type: &str, data: Value) -> Result<E, Permanent> {
match E::from_data(event_type, data) {
None => Err(Permanent::Other(Failure::new(
format!("no rule for event type {event_type:?}"),
None,
))),
Some(Err(cause)) => Err(Permanent::Other(Failure::new(
format!("decoding {event_type:?}"),
Some(Box::new(cause)),
))),
Some(Ok(event)) => Ok(event),
}
}