ddd-toolkit-core 0.2.1

Traits and reference implementations for Domain-Driven Design building blocks: entities, value objects, aggregates, repositories, and event dispatch.
Documentation
use crate::domain::EntityId;

/// Generates new [`EntityId`]s, e.g. for a new aggregate.
pub trait IdGenerator<Id: EntityId>: Send + Sync {
    /// Generates a new id.
    fn generate(&self) -> Id;
}

#[cfg(test)]
mod test {
    use std::{
        fmt::Display,
        sync::atomic::{AtomicU32, Ordering},
    };

    use crate::domain::ValueObject;

    use super::*;

    #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
    struct FooId(String);

    impl ValueObject for FooId {}

    impl Display for FooId {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            write!(f, "{}", self.0)
        }
    }

    impl EntityId for FooId {}

    struct SequentialFooIdGenerator {
        counter: AtomicU32,
    }

    impl SequentialFooIdGenerator {
        fn new() -> Self {
            Self {
                counter: AtomicU32::new(0),
            }
        }
    }

    impl IdGenerator<FooId> for SequentialFooIdGenerator {
        fn generate(&self) -> FooId {
            let n = self.counter.fetch_add(1, Ordering::Relaxed) + 1;
            FooId(format!("id-{n}"))
        }
    }

    #[test]
    fn generate_returns_id_from_generator_impl() {
        let generator = SequentialFooIdGenerator::new();

        assert_eq!(generator.generate(), FooId("id-1".to_string()));
        assert_eq!(generator.generate(), FooId("id-2".to_string()));
    }

    #[test]
    fn generic_fn_accepts_id_generator_impl() {
        fn next_id<G: IdGenerator<FooId>>(generator: &G) -> FooId {
            generator.generate()
        }

        let generator = SequentialFooIdGenerator::new();

        assert_eq!(next_id(&generator), FooId("id-1".to_string()));
    }

    #[test]
    fn box_dyn_id_generator_is_usable() {
        let generator: Box<dyn IdGenerator<FooId>> = Box::new(SequentialFooIdGenerator::new());

        assert_eq!(generator.generate(), FooId("id-1".to_string()));
    }

    #[test]
    fn id_generator_bound_implies_send_sync() {
        fn assert_send_sync<T: Send + Sync>() {}
        fn check<G: IdGenerator<FooId>>() {
            assert_send_sync::<G>();
        }

        check::<SequentialFooIdGenerator>();
    }
}