1 //! The implementation for Version 7 UUIDs.
2 //!
3 //! Note that you need to enable the `v7` Cargo feature
4 //! in order to use this module.
5 
6 use crate::{rng, std::convert::TryInto, timestamp::Timestamp, Builder, Uuid};
7 
8 impl Uuid {
9     /// Create a new version 7 UUID using the current time value and random bytes.
10     ///
11     /// This method is a convenient alternative to [`Uuid::new_v7`] that uses the current system time
12     /// as the source timestamp.
13     #[cfg(feature = "std")]
now_v7() -> Self14     pub fn now_v7() -> Self {
15         Self::new_v7(Timestamp::now(crate::NoContext))
16     }
17 
18     /// Create a new version 7 UUID using a time value and random bytes.
19     ///
20     /// When the `std` feature is enabled, you can also use [`Uuid::now_v7`].
21     ///
22     /// Note that usage of this method requires the `v7` feature of this crate
23     /// to be enabled.
24     ///
25     /// Also see [`Uuid::now_v7`] for a convenient way to generate version 7
26     /// UUIDs using the current system time.
27     ///
28     /// # Examples
29     ///
30     /// A v7 UUID can be created from a unix [`Timestamp`] plus a 128 bit
31     /// random number. When supplied as such, the data will be
32     ///
33     /// ```rust
34     /// # use uuid::{Uuid, Timestamp, NoContext};
35     /// let ts = Timestamp::from_unix(NoContext, 1497624119, 1234);
36     ///
37     /// let uuid = Uuid::new_v7(ts);
38     ///
39     /// assert!(
40     ///     uuid.hyphenated().to_string().starts_with("015cb15a-86d8-7")
41     /// );
42     /// ```
43     ///
44     /// # References
45     ///
46     /// * [Version 7 UUIDs in Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.2)
new_v7(ts: Timestamp) -> Self47     pub fn new_v7(ts: Timestamp) -> Self {
48         let (secs, nanos) = ts.to_unix();
49         let millis = (secs * 1000).saturating_add(nanos as u64 / 1_000_000);
50 
51         Builder::from_unix_timestamp_millis(millis, &rng::bytes()[..10].try_into().unwrap())
52             .into_uuid()
53     }
54 }
55 
56 #[cfg(test)]
57 mod tests {
58     use super::*;
59     use crate::{std::string::ToString, NoContext, Variant, Version};
60     #[cfg(all(
61         target_arch = "wasm32",
62         target_vendor = "unknown",
63         target_os = "unknown"
64     ))]
65     use wasm_bindgen_test::*;
66 
67     #[test]
68     #[cfg_attr(
69         all(
70             target_arch = "wasm32",
71             target_vendor = "unknown",
72             target_os = "unknown"
73         ),
74         wasm_bindgen_test
75     )]
test_new()76     fn test_new() {
77         let ts: u64 = 1645557742000;
78 
79         let seconds = ts / 1000;
80         let nanos = ((ts % 1000) * 1_000_000) as u32;
81 
82         let uuid = Uuid::new_v7(Timestamp::from_unix(NoContext, seconds, nanos));
83         let uustr = uuid.hyphenated().to_string();
84 
85         assert_eq!(uuid.get_version(), Some(Version::SortRand));
86         assert_eq!(uuid.get_variant(), Variant::RFC4122);
87         assert!(uuid.hyphenated().to_string().starts_with("017f22e2-79b0-7"));
88 
89         // Ensure parsing the same UUID produces the same timestamp
90         let parsed = Uuid::parse_str(uustr.as_str()).unwrap();
91 
92         assert_eq!(uuid, parsed);
93     }
94 
95     #[test]
96     #[cfg_attr(
97         all(
98             target_arch = "wasm32",
99             target_vendor = "unknown",
100             target_os = "unknown"
101         ),
102         wasm_bindgen_test
103     )]
104     #[cfg(feature = "std")]
test_now()105     fn test_now() {
106         let uuid = Uuid::now_v7();
107 
108         assert_eq!(uuid.get_version(), Some(Version::SortRand));
109         assert_eq!(uuid.get_variant(), Variant::RFC4122);
110     }
111 
112     #[test]
113     #[cfg_attr(
114         all(
115             target_arch = "wasm32",
116             target_vendor = "unknown",
117             target_os = "unknown"
118         ),
119         wasm_bindgen_test
120     )]
test_sorting()121     fn test_sorting() {
122         let time1: u64 = 1_496_854_535;
123         let time_fraction1: u32 = 812_000_000;
124 
125         let time2 = time1 + 4000;
126         let time_fraction2 = time_fraction1;
127 
128         let uuid1 = Uuid::new_v7(Timestamp::from_unix(NoContext, time1, time_fraction1));
129         let uuid2 = Uuid::new_v7(Timestamp::from_unix(NoContext, time2, time_fraction2));
130 
131         assert!(uuid1.as_bytes() < uuid2.as_bytes());
132         assert!(uuid1.to_string() < uuid2.to_string());
133     }
134 
135     #[test]
136     #[cfg_attr(
137         all(
138             target_arch = "wasm32",
139             target_vendor = "unknown",
140             target_os = "unknown"
141         ),
142         wasm_bindgen_test
143     )]
test_new_timestamp_roundtrip()144     fn test_new_timestamp_roundtrip() {
145         let time: u64 = 1_496_854_535;
146         let time_fraction: u32 = 812_000_000;
147 
148         let ts = Timestamp::from_unix(NoContext, time, time_fraction);
149 
150         let uuid = Uuid::new_v7(ts);
151 
152         let decoded_ts = uuid.get_timestamp().unwrap();
153 
154         assert_eq!(ts.to_unix(), decoded_ts.to_unix());
155     }
156 }
157