1use std::sync::atomic::{AtomicU64, Ordering};
2
3use chrono::{DateTime, Utc};
4use rand::Rng;
5use uuid::Uuid;
6
7static LAST_TIMESTAMP: AtomicU64 = AtomicU64::new(0);
8
9pub fn gen_uuid_v7() -> Uuid {
10 let now: DateTime<Utc> = Utc::now();
11 let mut timestamp = now.timestamp_millis() as u64;
12
13 loop {
14 let last_timestamp = LAST_TIMESTAMP.load(Ordering::SeqCst);
15
16 if timestamp <= last_timestamp {
17 timestamp = last_timestamp + 1;
18 }
19
20 if LAST_TIMESTAMP
21 .compare_exchange(
22 last_timestamp,
23 timestamp,
24 Ordering::SeqCst,
25 Ordering::SeqCst,
26 )
27 .is_ok()
28 {
29 break;
30 }
31 }
32
33 let mut bytes = [0u8; 16];
36
37 let timestamp_bytes = timestamp.to_be_bytes();
39 bytes[0..6].copy_from_slice(×tamp_bytes[2..8]);
40
41 let mut rng = rand::thread_rng();
43 bytes[6..].copy_from_slice(&rng.gen::<[u8; 10]>());
44
45 bytes[6] &= 0x0F; bytes[6] |= 0x70; bytes[8] &= 0xBF; bytes[8] |= 0x80;
50
51 Uuid::from_bytes(bytes)
52}
53
54#[cfg(test)]
55mod tests {
56 use std::collections::HashSet;
57
58 use uuid::Uuid;
59
60 use super::gen_uuid_v7;
61
62 #[test]
63 fn test_uuid_validity() {
64 let uuid = gen_uuid_v7();
66
67 assert!(Uuid::parse_str(&uuid.to_string()).is_ok());
69 }
70
71 #[test]
72 fn test_uuid_uniqueness() {
73 let mut set = HashSet::new();
74 for _ in 0..1000 {
75 let uuid = gen_uuid_v7();
77
78 assert!(set.insert(uuid));
80 }
81 }
82
83 #[test]
84 fn test_uuid_version() {
85 let uuid = gen_uuid_v7();
87
88 assert_eq!(uuid.get_version_num(), 7);
90 }
91
92 #[test]
93 fn test_uuid_increasing_many() {
94 let uuids = (0..1000).map(|_| gen_uuid_v7()).collect::<Vec<_>>();
95 for i in 1..uuids.len() {
96 assert!(
97 uuids[i] > uuids[i - 1],
98 "UUIDs are not monotonically increasing"
99 );
100 }
101 }
102}