1use serde::{Deserialize, Serialize};
2use std::sync::atomic::{AtomicU64, Ordering};
3use uuid::Uuid;
4
5#[derive(Debug, Serialize, Deserialize)]
29pub struct UuidGenerator {
30 namespace: Uuid,
31 counter: AtomicU64,
32}
33
34impl UuidGenerator {
53 pub fn new(namespace: Uuid) -> Self {
65 Self {
66 namespace,
67 counter: AtomicU64::new(0),
68 }
69 }
70
71 pub fn next(&self) -> Uuid {
80 let counter = self.counter.fetch_add(1, Ordering::SeqCst);
81 let name = counter.to_string();
82 Uuid::new_v5(&self.namespace, name.as_bytes())
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use std::collections::HashSet;
91 use std::sync::{Arc, Barrier};
92 use std::thread;
93
94 fn create_test_namespace() -> Uuid {
96 Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap()
97 }
98
99 #[test]
100 fn test_uuid_generator_creation() {
101 let namespace = create_test_namespace();
102 let generator = UuidGenerator::new(namespace);
103
104 assert_eq!(generator.namespace, namespace);
105 assert_eq!(generator.counter.load(Ordering::SeqCst), 0);
106 }
107
108 #[test]
109 fn test_uuid_generator_next() {
110 let generator = UuidGenerator::new(create_test_namespace());
111
112 let uuid1 = generator.next();
114 assert_eq!(generator.counter.load(Ordering::SeqCst), 1);
115
116 let uuid2 = generator.next();
118 assert_eq!(generator.counter.load(Ordering::SeqCst), 2);
119
120 assert_ne!(uuid1, uuid2);
122
123 assert_eq!(uuid1.get_version(), Some(uuid::Version::Sha1));
125 assert_eq!(uuid2.get_version(), Some(uuid::Version::Sha1));
126 }
127
128 #[test]
129 fn test_uuid_generator_deterministic() {
130 let namespace = create_test_namespace();
132 let generator1 = UuidGenerator::new(namespace);
133 let generator2 = UuidGenerator::new(namespace);
134
135 assert_eq!(generator1.next(), generator2.next());
137 assert_eq!(generator1.next(), generator2.next());
138 }
139
140 #[test]
141 fn test_uuid_generator_different_namespaces() {
142 let namespace1 = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap();
144 let namespace2 = Uuid::parse_str("6ba7b811-9dad-11d1-80b4-00c04fd430c8").unwrap();
145
146 let generator1 = UuidGenerator::new(namespace1);
147 let generator2 = UuidGenerator::new(namespace2);
148
149 assert_ne!(generator1.next(), generator2.next());
151 assert_ne!(generator1.next(), generator2.next());
152 }
153
154 #[test]
155 fn test_uuid_generator_sequential() {
156 let generator = UuidGenerator::new(create_test_namespace());
157 let mut uuids = Vec::new();
158
159 for _ in 0..100 {
161 uuids.push(generator.next());
162 }
163
164 let unique_uuids: HashSet<_> = uuids.iter().collect();
166 assert_eq!(unique_uuids.len(), 100);
167
168 assert_eq!(generator.counter.load(Ordering::SeqCst), 100);
170 }
171
172 #[test]
173 fn test_uuid_generator_thread_safety() {
174 let generator = Arc::new(UuidGenerator::new(create_test_namespace()));
175 let num_threads = 10;
176 let uuids_per_thread = 100;
177 let total_uuids = num_threads * uuids_per_thread;
178
179 let barrier = Arc::new(Barrier::new(num_threads));
181
182 let all_uuids = Arc::new(std::sync::Mutex::new(Vec::with_capacity(total_uuids)));
184
185 let mut handles = vec![];
186
187 for _ in 0..num_threads {
188 let thread_generator = Arc::clone(&generator);
189 let thread_barrier = Arc::clone(&barrier);
190 let thread_uuids = Arc::clone(&all_uuids);
191
192 let handle = thread::spawn(move || {
193 thread_barrier.wait(); let mut local_uuids = Vec::with_capacity(uuids_per_thread);
196 for _ in 0..uuids_per_thread {
197 local_uuids.push(thread_generator.next());
198 }
199
200 let mut all = thread_uuids.lock().unwrap();
202 all.extend(local_uuids);
203 });
204
205 handles.push(handle);
206 }
207
208 for handle in handles {
210 handle.join().unwrap();
211 }
212
213 let all_uuids = all_uuids.lock().unwrap();
215 let unique_uuids: HashSet<_> = all_uuids.iter().collect();
216
217 assert_eq!(
218 unique_uuids.len(),
219 total_uuids,
220 "All generated UUIDs should be unique"
221 );
222
223 assert_eq!(
225 generator.counter.load(Ordering::SeqCst),
226 total_uuids as u64,
227 "Counter should match the total number of generated UUIDs"
228 );
229 }
230
231 #[test]
232 fn test_uuid_generator_with_initial_counter() {
233 let namespace = create_test_namespace();
235 let initial_counter = 1000;
236
237 let mut generator = UuidGenerator::new(namespace);
238 generator.counter = AtomicU64::new(initial_counter);
239
240 let _ = generator.next();
242
243 assert_eq!(
245 generator.counter.load(Ordering::SeqCst),
246 initial_counter + 1
247 );
248
249 let mut generator2 = UuidGenerator::new(namespace);
251 generator2.counter = AtomicU64::new(initial_counter + 1);
252
253 assert_eq!(generator.next(), generator2.next());
255 }
256}