ddd_toolkit_core/adapter/
id.rs1use 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 UuidV4Generator,
40 uuid::Uuid::new_v4
41);
42
43uuid_id_generator!(
44 UuidV7Generator,
46 uuid::Uuid::now_v7
47);
48
49#[cfg(test)]
50mod test {
51 use std::fmt::Display;
52
53 use uuid::{Uuid, Variant};
54
55 use crate::domain::ValueObject;
56
57 use super::*;
58
59 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
60 struct UuidFooId(Uuid);
61
62 impl ValueObject for UuidFooId {}
63
64 impl Display for UuidFooId {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(f, "{}", self.0)
67 }
68 }
69
70 impl EntityId for UuidFooId {}
71
72 impl From<Uuid> for UuidFooId {
73 fn from(value: Uuid) -> Self {
74 Self(value)
75 }
76 }
77
78 #[test]
79 fn uuid_v4_generate_returns_version_4_rfc4122_uuid() {
80 let generator = UuidV4Generator::<UuidFooId>::new();
81
82 let id = generator.generate();
83
84 assert_eq!(id.0.get_version_num(), 4);
85 assert_eq!(id.0.get_variant(), Variant::RFC4122);
86 }
87
88 #[test]
89 fn uuid_v4_generate_twice_returns_distinct_ids() {
90 let generator = UuidV4Generator::<UuidFooId>::new();
91
92 assert_ne!(generator.generate(), generator.generate());
93 }
94
95 #[test]
96 fn uuid_v4_new_and_default_construct_usable_generator() {
97 let from_new = UuidV4Generator::<UuidFooId>::new();
98 let from_default = UuidV4Generator::<UuidFooId>::default();
99
100 assert_eq!(from_new.generate().0.get_version_num(), 4);
101 assert_eq!(from_default.generate().0.get_version_num(), 4);
102 }
103
104 #[test]
105 fn uuid_v4_generator_works_through_id_generator_bound() {
106 fn next_id<G: IdGenerator<UuidFooId>>(generator: &G) -> UuidFooId {
107 generator.generate()
108 }
109
110 let generator = UuidV4Generator::new();
111
112 assert_eq!(next_id(&generator).0.get_version_num(), 4);
113 }
114
115 #[test]
116 fn uuid_v7_generate_returns_version_7_rfc4122_uuid() {
117 let generator = UuidV7Generator::<UuidFooId>::new();
118
119 let id = generator.generate();
120
121 assert_eq!(id.0.get_version_num(), 7);
122 assert_eq!(id.0.get_variant(), Variant::RFC4122);
123 }
124
125 #[test]
126 fn uuid_v7_generate_twice_returns_distinct_ids() {
127 let generator = UuidV7Generator::<UuidFooId>::new();
128
129 assert_ne!(generator.generate(), generator.generate());
130 }
131
132 #[test]
133 fn uuid_v7_timestamps_are_non_decreasing() {
134 let generator = UuidV7Generator::<UuidFooId>::new();
135
136 let timestamps: Vec<_> = (0..10)
137 .map(|_| {
138 generator
139 .generate()
140 .0
141 .get_timestamp()
142 .expect("v7 uuid should embed a timestamp")
143 .to_unix()
144 })
145 .collect();
146
147 for pair in timestamps.windows(2) {
148 assert!(pair[0] <= pair[1], "timestamps went backwards: {pair:?}");
149 }
150 }
151
152 #[test]
153 fn uuid_generators_are_send_sync_and_clone() {
154 fn assert_impl<T: Send + Sync + Clone>() {}
155
156 assert_impl::<UuidV4Generator<UuidFooId>>();
157 assert_impl::<UuidV7Generator<UuidFooId>>();
158 }
159}