arts-n-crafts 0.1.3

Arts and Crafts was a reaction against the industrialization and excess of the Victorian era, and it sought to celebrate traditional craftsmanship and materials. Arts and Crafts architecture is characterized by a simple and functional design, the use of natural materials, and a focus on craftsmanship.
Documentation
use crate::core::base_payload::BasePayload;
use crate::domain::domain_event::DomainEvent;
use crate::domain::with_identifier::WithIdentifier;
use async_trait::async_trait;
use std::fmt::Debug;
use thiserror::Error;

#[derive(Debug, Error, PartialEq)]
pub enum RepositoryError {
    #[error("appending failed")]
    AppendFailed,

    #[error("loading failed")]
    LoadFailed,
}

#[async_trait]
pub trait Repository<TState, TEventPayload, TError>
where
    TState: Debug + PartialEq,
    TEventPayload: BasePayload,
    TError: std::error::Error + Send + Sync + 'static,
{
    async fn store(
        &self,
        events: Vec<DomainEvent<TEventPayload>>,
    ) -> Result<WithIdentifier, TError>;

    async fn load(&self, aggregate_id: String) -> Result<TState, TError>;
}

#[cfg(test)]
pub mod repository_tests {
    use super::*;
    use async_trait::async_trait;
    use rstest::rstest;
    use serde::{Deserialize, Serialize};
    use strum_macros::AsRefStr;
    use uuid::Uuid;

    #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
    pub struct User {
        pub id: String,
        pub name: String,
        pub likes: u8,
    }

    #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, AsRefStr)]
    pub enum UserEventPayload {
        UserCreated { id: String, name: String },
        UserLiked,
    }

    struct UserRepository;

    #[async_trait]
    impl Repository<User, UserEventPayload, RepositoryError> for UserRepository {
        async fn store(
            &self,
            events: Vec<DomainEvent<UserEventPayload>>,
        ) -> Result<WithIdentifier, RepositoryError> {
            Ok(WithIdentifier {
                id: events.first().cloned().unwrap().aggregate_id,
            })
        }

        async fn load(&self, aggregate_id: String) -> Result<User, RepositoryError> {
            Ok(User {
                id: aggregate_id,
                name: "John Doe".to_string(),
                likes: 0,
            })
        }
    }

    #[rstest]
    #[tokio::test]
    async fn it_should_store_and_return_the_id_of_the_user_subject() {
        let repository = UserRepository;
        let aggregate_id = Uuid::now_v7().to_string();
        let event = DomainEvent::create(
            aggregate_id.clone(),
            UserEventPayload::UserCreated {
                id: Uuid::now_v7().to_string(),
                name: "John Doe".to_string(),
            },
        );
        let result = repository.store(vec![event]).await.unwrap();
        assert_eq!(result, WithIdentifier { id: aggregate_id });
    }

    #[rstest]
    #[tokio::test]
    async fn it_should_load_the_user_with_given_id() {
        let repository = UserRepository;
        let aggregate_id = Uuid::now_v7().to_string();
        let result = repository.load(aggregate_id.clone()).await.unwrap();
        assert_eq!(
            result,
            User {
                id: aggregate_id,
                name: "John Doe".to_string(),
                likes: 0,
            }
        );
    }
}