uuid_v7/
lib.rs

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    // This bit was probably copied from some other repo, but I don't remember where I got it from
34    // anymore
35    let mut bytes = [0u8; 16];
36
37    // Fill the timestamp in the first 48 bits (6 bytes)
38    let timestamp_bytes = timestamp.to_be_bytes();
39    bytes[0..6].copy_from_slice(&timestamp_bytes[2..8]);
40
41    // Fill the remaining bytes with random data
42    let mut rng = rand::thread_rng();
43    bytes[6..].copy_from_slice(&rng.gen::<[u8; 10]>());
44
45    // Set the UUID version (0111 for v7) and variant (10)
46    bytes[6] &= 0x0F; // Clear the top 4 bits
47    bytes[6] |= 0x70; // Set the version (0111)
48    bytes[8] &= 0xBF; // Set the top 2 bits to 10
49    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        // Generate a UUID
65        let uuid = gen_uuid_v7();
66
67        // Check if it's a valid UUID
68        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            // Generate a UUID
76            let uuid = gen_uuid_v7();
77
78            // Insert into the set and ensure it's unique
79            assert!(set.insert(uuid));
80        }
81    }
82
83    #[test]
84    fn test_uuid_version() {
85        // Generate a UUID
86        let uuid = gen_uuid_v7();
87
88        // Check if it's version 7
89        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}