Skip to main content

rosetta_uuid/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use core::str::FromStr;
4
5pub mod diesel_impls;
6mod redis;
7
8#[repr(transparent)]
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
10#[cfg_attr(
11    feature = "diesel",
12    derive(diesel::expression::AsExpression, diesel::deserialize::FromSqlRow)
13)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[cfg_attr(feature = "diesel", diesel(sql_type = crate::diesel_impls::Uuid))]
16/// A wrapper around the `uuid` crate's `Uuid` type.
17///
18/// # Examples
19///
20/// ```
21/// use rosetta_uuid::Uuid;
22/// use std::str::FromStr;
23///
24/// let uuid = Uuid::new_v4();
25/// let parsed = Uuid::from_str(&uuid.to_string()).unwrap();
26/// assert_eq!(uuid, parsed);
27/// ```
28pub struct Uuid(uuid::Uuid);
29
30impl Uuid {
31    #[must_use]
32    /// Creates a new `Uuid` using the `uuid` crate's `new_v4` method.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// use rosetta_uuid::Uuid;
38    ///
39    /// let uuid = Uuid::new_v4();
40    /// ```
41    pub fn new_v4() -> Self {
42        Self(uuid::Uuid::new_v4())
43    }
44
45    #[must_use]
46    /// Creates a new `Uuid` using the `uuid` crate's `new_v7` method with the current UTC timestamp.
47    ///
48    /// # Examples
49    ///
50    /// ```
51    /// use rosetta_uuid::Uuid;
52    ///
53    /// let uuid = Uuid::utc_v7();
54    /// ```
55    pub fn utc_v7() -> Self {
56        let utc_now = ::chrono::Utc::now();
57        ::uuid::Uuid::new_v7(::uuid::Timestamp::from_unix_time(
58            u64::try_from(utc_now.timestamp()).expect("Time went backwards"),
59            utc_now.timestamp_subsec_nanos(),
60            0,
61            12,
62        ))
63        .into()
64    }
65}
66
67impl Default for Uuid {
68    fn default() -> Self {
69        Self(uuid::Uuid::nil())
70    }
71}
72
73impl FromStr for Uuid {
74    type Err = uuid::Error;
75
76    fn from_str(s: &str) -> Result<Self, Self::Err> {
77        Ok(Self(uuid::Uuid::from_str(s)?))
78    }
79}
80
81impl From<uuid::Uuid> for Uuid {
82    fn from(uuid: uuid::Uuid) -> Self {
83        Self(uuid)
84    }
85}
86
87impl From<Uuid> for uuid::Uuid {
88    fn from(uuid: Uuid) -> Self {
89        uuid.0
90    }
91}
92
93impl From<[u8; 16]> for Uuid {
94    fn from(bytes: [u8; 16]) -> Self {
95        Self(uuid::Uuid::from_bytes(bytes))
96    }
97}
98
99impl From<Uuid> for [u8; 16] {
100    fn from(uuid: Uuid) -> Self {
101        *uuid.0.as_bytes()
102    }
103}
104
105impl<'a> From<&'a [u8; 16]> for Uuid {
106    fn from(bytes: &'a [u8; 16]) -> Self {
107        Self(uuid::Uuid::from_bytes(*bytes))
108    }
109}
110
111impl AsRef<uuid::Uuid> for Uuid {
112    fn as_ref(&self) -> &uuid::Uuid {
113        &self.0
114    }
115}
116
117impl AsMut<uuid::Uuid> for Uuid {
118    fn as_mut(&mut self) -> &mut uuid::Uuid {
119        &mut self.0
120    }
121}
122
123impl AsRef<[u8; 16]> for Uuid {
124    fn as_ref(&self) -> &[u8; 16] {
125        self.0.as_bytes()
126    }
127}
128
129impl core::ops::Deref for Uuid {
130    type Target = uuid::Uuid;
131
132    fn deref(&self) -> &Self::Target {
133        &self.0
134    }
135}
136
137impl core::ops::DerefMut for Uuid {
138    fn deref_mut(&mut self) -> &mut Self::Target {
139        &mut self.0
140    }
141}
142
143impl core::fmt::Display for Uuid {
144    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> std::fmt::Result {
145        self.0.fmt(f)
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152    use std::cmp::Ordering;
153    use std::collections::HashSet;
154    use std::str::FromStr;
155
156    #[test]
157    fn test_default() {
158        let uuid = Uuid::default();
159        assert_eq!(uuid.0, uuid::Uuid::nil());
160        assert_eq!(uuid.to_string(), "00000000-0000-0000-0000-000000000000");
161    }
162
163    #[test]
164    fn test_from_str() {
165        let s = "67e55044-10b1-426f-9247-bb680e5fe0c8";
166        let uuid = Uuid::from_str(s).unwrap();
167        assert_eq!(uuid.to_string(), s);
168
169        let invalid = "invalid-uuid";
170        assert!(Uuid::from_str(invalid).is_err());
171    }
172
173    #[test]
174    fn test_from_conversions() {
175        let inner = uuid::Uuid::new_v4();
176        let wrapper: Uuid = inner.into();
177        assert_eq!(wrapper.0, inner);
178
179        let back: uuid::Uuid = wrapper.into();
180        assert_eq!(back, inner);
181
182        let bytes = [0u8; 16];
183        let from_bytes: Uuid = bytes.into();
184        assert_eq!(from_bytes.0, uuid::Uuid::nil());
185
186        let bytes_back: [u8; 16] = from_bytes.into();
187        assert_eq!(bytes_back, bytes);
188
189        let bytes_ref = &[0u8; 16];
190        let from_bytes_ref: Uuid = bytes_ref.into();
191        assert_eq!(from_bytes_ref.0, uuid::Uuid::nil());
192    }
193
194    #[test]
195    fn test_as_ref_as_mut() {
196        let mut uuid = Uuid::new_v4();
197
198        let r: &uuid::Uuid = uuid.as_ref();
199        assert_eq!(*r, uuid.0);
200
201        let bytes_ref: &[u8; 16] = uuid.as_ref();
202        assert_eq!(bytes_ref, uuid.0.as_bytes());
203
204        let m: &mut uuid::Uuid = uuid.as_mut();
205        // Modify existing to something known
206        *m = uuid::Uuid::nil();
207        assert_eq!(uuid.0, uuid::Uuid::nil());
208    }
209
210    #[test]
211    fn test_deref_deref_mut() {
212        let mut uuid = Uuid::new_v4();
213
214        // Deref
215        assert_eq!(uuid.get_version(), Some(uuid::Version::Random));
216
217        // DerefMut (modifying internally to nil just as a test)
218        *uuid = uuid::Uuid::nil();
219        assert!(uuid.is_nil());
220    }
221
222    #[test]
223    fn test_display() {
224        let s = "67e55044-10b1-426f-9247-bb680e5fe0c8";
225        let uuid = Uuid::from_str(s).unwrap();
226        assert_eq!(format!("{uuid}"), s);
227    }
228
229    #[test]
230    fn test_standard_traits() {
231        let uuid1 = Uuid::new_v4();
232        let uuid2 = Clone::clone(&uuid1); // Clone
233        let uuid3 = uuid1; // Copy
234
235        assert_eq!(uuid1, uuid2); // PartialEq
236        assert_eq!(uuid1, uuid3); // PartialEq
237        assert!(uuid1 == uuid2); // Eq check implicitly
238
239        let mut set = HashSet::new();
240        set.insert(uuid1); // Hash
241        assert!(set.contains(&uuid2));
242    }
243
244    #[test]
245    fn test_ord() {
246        let u1 = Uuid::default(); // nil, all zeros
247        let u2 = Uuid::new_v4(); // random
248
249        assert_eq!(u1.cmp(&u2), Ordering::Less); // Ord
250        assert!(u1 < u2); // PartialOrd
251    }
252
253    #[test]
254    fn test_new_constructors() {
255        let v4 = Uuid::new_v4();
256        assert_eq!(v4.get_version(), Some(uuid::Version::Random));
257
258        let v7 = Uuid::utc_v7();
259        // Version v7 might check differently depending on uuid crate version
260        // assert_eq!(v7.get_version(), Some(uuid::Version::Sortable));
261        assert!(!v7.is_nil());
262    }
263}