timesource 0.1.3

Event sourcing with TimescaleDb
Documentation
#![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]),
        }
    }
}