#![allow(dead_code)] use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter, Result as FmtResult}; use timesource::{Aggregate, TimesourceEvent}; use uuid::Uuid; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct OrderItem { pub item_sku: String, pub quantity: u8, pub price: i32, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum OrderState { Editable { updated_at: DateTime }, Complete { at: DateTime }, Cancelled { at: DateTime }, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Order { pub id: Uuid, pub created_at: DateTime, pub items: Vec, pub state: OrderState, } impl Order { pub fn created_at(&self) -> DateTime { self.created_at } pub fn items(&self) -> &Vec { &self.items } pub fn state(&self) -> OrderState { self.state } pub fn is_editable(&self) -> bool { if let OrderState::Editable { .. } = self.state { return true; } false } } #[derive(Debug, Clone)] pub enum OrderCommand { Create, AddItem { item: OrderItem }, Empty(String), Complete, Cancel, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, TimesourceEvent)] pub enum OrderEvent { Created, ItemAdded { item: OrderItem }, Emptied(String), Completed, Cancelled, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum OrderError { AlreadyCreated, NotYetCreated, NotEditable, AlreadyCompleted, AlreadyCancelled, UnexpectedData, } impl Display for OrderError { fn fmt(&self, fmt: &mut Formatter) -> FmtResult { match self { OrderError::AlreadyCreated => write!(fmt, "order has already been created"), OrderError::NotYetCreated => write!(fmt, "order has not been created yet"), OrderError::NotEditable => write!(fmt, "order can't be edited anymore"), OrderError::AlreadyCancelled => write!(fmt, "order has already been cancelled"), OrderError::AlreadyCompleted => write!(fmt, "order has already been completed"), OrderError::UnexpectedData => write!(fmt, "unexpected data received"), } } } impl std::error::Error for OrderError {} #[derive(Debug, Default, Clone, Copy)] pub struct OrderAggregate; impl Aggregate for OrderAggregate { type State = Order; type Event = OrderEvent; type Command = OrderCommand; type Error = OrderError; fn apply_first( id: Uuid, event: &Self::Event, created_at: DateTime, _event_id: Option, ) -> Result { if let OrderEvent::Created = event { return Ok(Order { id, created_at, items: Vec::new(), state: OrderState::Editable { updated_at: created_at, }, }); } Err(OrderError::NotYetCreated) } fn apply_next( state: &mut Self::State, event: &Self::Event, at: DateTime, _event_id: Option, ) -> Result<(), Self::Error> { match event { OrderEvent::Created { .. } => Err(OrderError::AlreadyCreated), OrderEvent::ItemAdded { item } => { if let OrderState::Editable { .. } = state.state { state.state = OrderState::Editable { updated_at: at }; state .items .iter_mut() .find(|it| item.item_sku == it.item_sku) .map(|it| it.quantity += item.quantity) .or_else(|| { state.items.push(item.clone()); Some(()) }); return Ok(()); } Err(OrderError::NotEditable) } OrderEvent::Emptied(_) => { state.items = vec![]; Ok(()) } OrderEvent::Completed => { if let OrderState::Complete { .. } = state.state { return Err(OrderError::AlreadyCompleted); } if let OrderState::Editable { .. } = state.state { state.state = OrderState::Complete { at }; return Ok(()); } Err(OrderError::NotEditable) } OrderEvent::Cancelled => { if let OrderState::Cancelled { .. } = state.state { return Err(OrderError::AlreadyCancelled); } if let OrderState::Editable { .. } = state.state { state.state = OrderState::Cancelled { at }; return Ok(()); } Err(OrderError::NotEditable) } } } fn handle_first(command: Self::Command) -> Result, Self::Error> { if let OrderCommand::Create = command { return Ok(vec![OrderEvent::Created]); } Err(OrderError::NotYetCreated) } fn handle_next( _state: &Self::State, command: Self::Command, ) -> Result, Self::Error> { match command { OrderCommand::Create => Err(OrderError::AlreadyCreated), OrderCommand::AddItem { item } => Ok(vec![OrderEvent::ItemAdded { item }]), OrderCommand::Empty(reason) => Ok(vec![OrderEvent::Emptied(reason)]), OrderCommand::Complete => Ok(vec![OrderEvent::Completed]), OrderCommand::Cancel => Ok(vec![OrderEvent::Cancelled]), } } }