#![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<Utc> },
Complete { at: DateTime<Utc> },
Cancelled { at: DateTime<Utc> },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Order {
pub id: Uuid,
pub created_at: DateTime<Utc>,
pub items: Vec<OrderItem>,
pub state: OrderState,
}
impl Order {
pub fn created_at(&self) -> DateTime<Utc> {
self.created_at
}
pub fn items(&self) -> &Vec<OrderItem> {
&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<Utc>,
_event_id: Option<u64>,
) -> Result<Self::State, Self::Error> {
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<Utc>,
_event_id: Option<u64>,
) -> 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<Vec<Self::Event>, 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<Vec<Self::Event>, 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]),
}
}
}