Skip to main content

ddd_toolkit_core/adapter/
id.rs

1use crate::domain::EntityId;
2use crate::port::id::IdGenerator;
3
4macro_rules! uuid_id_generator {
5    ($(#[$doc:meta])* $name:ident, $make:path) => {
6        $(#[$doc])*
7        #[derive(Debug, Clone)]
8        pub struct $name<Id>(core::marker::PhantomData<fn() -> Id>);
9
10        impl<Id> $name<Id> {
11            #[doc = concat!("Creates a new `", stringify!($name), "`.")]
12            pub fn new() -> Self {
13                Self(core::marker::PhantomData)
14            }
15        }
16
17        impl<Id> Default for $name<Id> {
18            fn default() -> Self {
19                Self::new()
20            }
21        }
22
23        impl<Id> IdGenerator<Id> for $name<Id>
24        where
25            Id: EntityId + TryFrom<uuid::Uuid, Error = core::convert::Infallible>,
26        {
27            fn generate(&self) -> Id {
28                match Id::try_from($make()) {
29                    Ok(id) => id,
30                    Err(never) => match never {},
31                }
32            }
33        }
34    };
35}
36
37uuid_id_generator!(
38    /// Generates ids from a random (v4) UUID.
39    ///
40    /// # Examples
41    ///
42    /// ```
43    /// use ddd_toolkit_core::adapter::id::UuidV4Generator;
44    /// use ddd_toolkit_core::domain::{EntityId, ValueObject};
45    /// use ddd_toolkit_core::port::id::IdGenerator;
46    /// use std::convert::Infallible;
47    /// use std::fmt::Display;
48    ///
49    /// #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
50    /// struct OrderId(uuid::Uuid);
51    ///
52    /// impl ValueObject for OrderId {}
53    ///
54    /// impl Display for OrderId {
55    ///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56    ///         write!(f, "{}", self.0)
57    ///     }
58    /// }
59    ///
60    /// impl EntityId for OrderId {}
61    ///
62    /// impl TryFrom<uuid::Uuid> for OrderId {
63    ///     type Error = Infallible;
64    ///     fn try_from(value: uuid::Uuid) -> Result<Self, Self::Error> {
65    ///         Ok(Self(value))
66    ///     }
67    /// }
68    ///
69    /// let generator = UuidV4Generator::<OrderId>::new();
70    ///
71    /// // two calls never collide
72    /// assert_ne!(generator.generate(), generator.generate());
73    /// ```
74    UuidV4Generator,
75    uuid::Uuid::new_v4
76);
77
78uuid_id_generator!(
79    /// Generates ids from a time-ordered (v7) UUID.
80    ///
81    /// # Examples
82    ///
83    /// ```
84    /// use ddd_toolkit_core::adapter::id::UuidV7Generator;
85    /// use ddd_toolkit_core::domain::{EntityId, ValueObject};
86    /// use ddd_toolkit_core::port::id::IdGenerator;
87    /// use std::convert::Infallible;
88    /// use std::fmt::Display;
89    ///
90    /// #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
91    /// struct OrderId(uuid::Uuid);
92    ///
93    /// impl ValueObject for OrderId {}
94    ///
95    /// impl Display for OrderId {
96    ///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97    ///         write!(f, "{}", self.0)
98    ///     }
99    /// }
100    ///
101    /// impl EntityId for OrderId {}
102    ///
103    /// impl TryFrom<uuid::Uuid> for OrderId {
104    ///     type Error = Infallible;
105    ///     fn try_from(value: uuid::Uuid) -> Result<Self, Self::Error> {
106    ///         Ok(Self(value))
107    ///     }
108    /// }
109    ///
110    /// let generator = UuidV7Generator::<OrderId>::new();
111    ///
112    /// // v7 ids are time-ordered, so ids minted later sort after earlier ones
113    /// let first = generator.generate();
114    /// let second = generator.generate();
115    /// assert!(first <= second);
116    /// ```
117    UuidV7Generator,
118    uuid::Uuid::now_v7
119);
120
121#[cfg(test)]
122mod test {
123    use std::fmt::Display;
124
125    use uuid::{Uuid, Variant};
126
127    use crate::domain::ValueObject;
128
129    use super::*;
130
131    #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
132    struct UuidFooId(Uuid);
133
134    impl ValueObject for UuidFooId {}
135
136    impl Display for UuidFooId {
137        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138            write!(f, "{}", self.0)
139        }
140    }
141
142    impl EntityId for UuidFooId {}
143
144    impl From<Uuid> for UuidFooId {
145        fn from(value: Uuid) -> Self {
146            Self(value)
147        }
148    }
149
150    #[test]
151    fn uuid_v4_generate_returns_version_4_rfc4122_uuid() {
152        let generator = UuidV4Generator::<UuidFooId>::new();
153
154        let id = generator.generate();
155
156        assert_eq!(id.0.get_version_num(), 4);
157        assert_eq!(id.0.get_variant(), Variant::RFC4122);
158    }
159
160    #[test]
161    fn uuid_v4_generate_twice_returns_distinct_ids() {
162        let generator = UuidV4Generator::<UuidFooId>::new();
163
164        assert_ne!(generator.generate(), generator.generate());
165    }
166
167    #[test]
168    fn uuid_v4_new_and_default_construct_usable_generator() {
169        let from_new = UuidV4Generator::<UuidFooId>::new();
170        let from_default = UuidV4Generator::<UuidFooId>::default();
171
172        assert_eq!(from_new.generate().0.get_version_num(), 4);
173        assert_eq!(from_default.generate().0.get_version_num(), 4);
174    }
175
176    #[test]
177    fn uuid_v4_generator_works_through_id_generator_bound() {
178        fn next_id<G: IdGenerator<UuidFooId>>(generator: &G) -> UuidFooId {
179            generator.generate()
180        }
181
182        let generator = UuidV4Generator::new();
183
184        assert_eq!(next_id(&generator).0.get_version_num(), 4);
185    }
186
187    #[test]
188    fn uuid_v7_generate_returns_version_7_rfc4122_uuid() {
189        let generator = UuidV7Generator::<UuidFooId>::new();
190
191        let id = generator.generate();
192
193        assert_eq!(id.0.get_version_num(), 7);
194        assert_eq!(id.0.get_variant(), Variant::RFC4122);
195    }
196
197    #[test]
198    fn uuid_v7_generate_twice_returns_distinct_ids() {
199        let generator = UuidV7Generator::<UuidFooId>::new();
200
201        assert_ne!(generator.generate(), generator.generate());
202    }
203
204    #[test]
205    fn uuid_v7_timestamps_are_non_decreasing() {
206        let generator = UuidV7Generator::<UuidFooId>::new();
207
208        let timestamps: Vec<_> = (0..10)
209            .map(|_| {
210                generator
211                    .generate()
212                    .0
213                    .get_timestamp()
214                    .expect("v7 uuid should embed a timestamp")
215                    .to_unix()
216            })
217            .collect();
218
219        for pair in timestamps.windows(2) {
220            assert!(pair[0] <= pair[1], "timestamps went backwards: {pair:?}");
221        }
222    }
223
224    #[test]
225    fn uuid_generators_are_send_sync_and_clone() {
226        fn assert_impl<T: Send + Sync + Clone>() {}
227
228        assert_impl::<UuidV4Generator<UuidFooId>>();
229        assert_impl::<UuidV7Generator<UuidFooId>>();
230    }
231}