/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ulid-1.2.1/src/lib.rs
Line | Count | Source |
1 | | #![warn(missing_docs)] |
2 | | //! # ulid-rs |
3 | | //! |
4 | | //! This is a Rust implementation of the [ulid][ulid] project which provides |
5 | | //! Universally Unique Lexicographically Sortable Identifiers. |
6 | | //! |
7 | | //! [ulid]: https://github.com/ulid/spec |
8 | | //! |
9 | | //! |
10 | | //! ## Quickstart |
11 | | //! |
12 | | //! ```rust |
13 | | //! # use ulid::Ulid; |
14 | | //! // Generate a ulid |
15 | | //! # #[cfg(not(feature = "std"))] |
16 | | //! # let ulid = Ulid::default(); |
17 | | //! # #[cfg(feature = "std")] |
18 | | //! let ulid = Ulid::new(); |
19 | | //! |
20 | | //! // Generate a string for a ulid |
21 | | //! let s = ulid.to_string(); |
22 | | //! |
23 | | //! // Create from a String |
24 | | //! let res = Ulid::from_string(&s); |
25 | | //! assert_eq!(ulid, res.unwrap()); |
26 | | //! |
27 | | //! // Or using FromStr |
28 | | //! let res = s.parse(); |
29 | | //! assert_eq!(ulid, res.unwrap()); |
30 | | //! |
31 | | //! ``` |
32 | | #![cfg_attr(not(feature = "std"), no_std)] |
33 | | |
34 | | #[doc = include_str!("../README.md")] |
35 | | #[cfg(all(doctest, feature = "std"))] |
36 | | struct ReadMeDoctest; |
37 | | |
38 | | mod base32; |
39 | | #[cfg(feature = "std")] |
40 | | mod generator; |
41 | | #[cfg(feature = "postgres")] |
42 | | mod postgres; |
43 | | #[cfg(feature = "rkyv")] |
44 | | mod rkyv; |
45 | | #[cfg(feature = "serde")] |
46 | | pub mod serde; |
47 | | #[cfg(feature = "std")] |
48 | | mod time; |
49 | | #[cfg(feature = "std")] |
50 | | mod time_utils; |
51 | | #[cfg(feature = "uuid")] |
52 | | mod uuid; |
53 | | |
54 | | use core::convert::TryFrom; |
55 | | use core::fmt; |
56 | | use core::str::FromStr; |
57 | | |
58 | | pub use crate::base32::{DecodeError, EncodeError, ULID_LEN}; |
59 | | #[cfg(feature = "std")] |
60 | | pub use crate::generator::{Generator, MonotonicError}; |
61 | | |
62 | | /// Create a right-aligned bitmask of $len bits |
63 | | macro_rules! bitmask { |
64 | | ($len:expr) => { |
65 | | ((1 << $len) - 1) |
66 | | }; |
67 | | } |
68 | | // Allow other modules to use the macro |
69 | | pub(crate) use bitmask; |
70 | | |
71 | | /// A Ulid is a unique 128-bit lexicographically sortable identifier |
72 | | /// |
73 | | /// Canonically, it is represented as a 26 character Crockford Base32 encoded |
74 | | /// string. |
75 | | /// |
76 | | /// Of the 128-bits, the first 48 are a unix timestamp in milliseconds. The |
77 | | /// remaining 80 are random. The first 48 provide for lexicographic sorting and |
78 | | /// the remaining 80 ensure that the identifier is unique. |
79 | | #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)] |
80 | | #[cfg_attr( |
81 | | feature = "rkyv", |
82 | | derive(::rkyv::Archive, ::rkyv::Serialize, ::rkyv::Deserialize) |
83 | | )] |
84 | | pub struct Ulid(pub u128); |
85 | | |
86 | | impl Ulid { |
87 | | /// The number of bits in a Ulid's time portion |
88 | | pub const TIME_BITS: u8 = 48; |
89 | | /// The number of bits in a Ulid's random portion |
90 | | pub const RAND_BITS: u8 = 80; |
91 | | |
92 | | /// Create a Ulid from separated parts. |
93 | | /// |
94 | | /// NOTE: Any overflow bits in the given args are discarded |
95 | | /// |
96 | | /// # Example |
97 | | /// ```rust |
98 | | /// use ulid::Ulid; |
99 | | /// |
100 | | /// let ulid = Ulid::from_string("01D39ZY06FGSCTVN4T2V9PKHFZ").unwrap(); |
101 | | /// |
102 | | /// let ulid2 = Ulid::from_parts(ulid.timestamp_ms(), ulid.random()); |
103 | | /// |
104 | | /// assert_eq!(ulid, ulid2); |
105 | | /// ``` |
106 | 0 | pub const fn from_parts(timestamp_ms: u64, random: u128) -> Ulid { |
107 | 0 | let time_part = (timestamp_ms & bitmask!(Self::TIME_BITS)) as u128; |
108 | 0 | let rand_part = random & bitmask!(Self::RAND_BITS); |
109 | 0 | Ulid((time_part << Self::RAND_BITS) | rand_part) |
110 | 0 | } |
111 | | |
112 | | /// Creates a Ulid from a Crockford Base32 encoded string |
113 | | /// |
114 | | /// An DecodeError will be returned when the given string is not formatted |
115 | | /// properly. |
116 | | /// |
117 | | /// # Example |
118 | | /// ```rust |
119 | | /// use ulid::Ulid; |
120 | | /// |
121 | | /// let text = "01D39ZY06FGSCTVN4T2V9PKHFZ"; |
122 | | /// let result = Ulid::from_string(text); |
123 | | /// |
124 | | /// assert!(result.is_ok()); |
125 | | /// assert_eq!(&result.unwrap().to_string(), text); |
126 | | /// ``` |
127 | 0 | pub const fn from_string(encoded: &str) -> Result<Ulid, DecodeError> { |
128 | 0 | match base32::decode(encoded) { |
129 | 0 | Ok(int_val) => Ok(Ulid(int_val)), |
130 | 0 | Err(err) => Err(err), |
131 | | } |
132 | 0 | } |
133 | | |
134 | | /// The 'nil Ulid'. |
135 | | /// |
136 | | /// The nil Ulid is special form of Ulid that is specified to have |
137 | | /// all 128 bits set to zero. |
138 | | /// |
139 | | /// # Example |
140 | | /// ```rust |
141 | | /// use ulid::Ulid; |
142 | | /// |
143 | | /// let ulid = Ulid::nil(); |
144 | | /// |
145 | | /// assert_eq!( |
146 | | /// ulid.to_string(), |
147 | | /// "00000000000000000000000000" |
148 | | /// ); |
149 | | /// ``` |
150 | 0 | pub const fn nil() -> Ulid { |
151 | 0 | Ulid(0) |
152 | 0 | } |
153 | | |
154 | | /// Gets the timestamp section of this ulid |
155 | | /// |
156 | | /// # Example |
157 | | /// ```rust |
158 | | /// # #[cfg(feature = "std")] { |
159 | | /// use std::time::{SystemTime, Duration}; |
160 | | /// use ulid::Ulid; |
161 | | /// |
162 | | /// let dt = SystemTime::now(); |
163 | | /// let ulid = Ulid::from_datetime(dt); |
164 | | /// |
165 | | /// assert_eq!(u128::from(ulid.timestamp_ms()), dt.duration_since(SystemTime::UNIX_EPOCH).unwrap_or(Duration::ZERO).as_millis()); |
166 | | /// # } |
167 | | /// ``` |
168 | 0 | pub const fn timestamp_ms(&self) -> u64 { |
169 | 0 | (self.0 >> Self::RAND_BITS) as u64 |
170 | 0 | } |
171 | | |
172 | | /// Gets the random section of this ulid |
173 | | /// |
174 | | /// # Example |
175 | | /// ```rust |
176 | | /// use ulid::Ulid; |
177 | | /// |
178 | | /// let text = "01D39ZY06FGSCTVN4T2V9PKHFZ"; |
179 | | /// let ulid = Ulid::from_string(text).unwrap(); |
180 | | /// let ulid_next = ulid.increment().unwrap(); |
181 | | /// |
182 | | /// assert_eq!(ulid.random() + 1, ulid_next.random()); |
183 | | /// ``` |
184 | 0 | pub const fn random(&self) -> u128 { |
185 | 0 | self.0 & bitmask!(Self::RAND_BITS) |
186 | 0 | } |
187 | | |
188 | | /// Creates a Crockford Base32 encoded string that represents this Ulid |
189 | | /// |
190 | | /// # Example |
191 | | /// ```rust |
192 | | /// use ulid::Ulid; |
193 | | /// |
194 | | /// let text = "01D39ZY06FGSCTVN4T2V9PKHFZ"; |
195 | | /// let ulid = Ulid::from_string(text).unwrap(); |
196 | | /// |
197 | | /// let mut buf = [0; ulid::ULID_LEN]; |
198 | | /// let new_text = ulid.to_str(&mut buf).unwrap(); |
199 | | /// |
200 | | /// assert_eq!(new_text, text); |
201 | | /// ``` |
202 | | #[deprecated(since = "1.2.0", note = "Use the infallible `array_to_str` instead.")] |
203 | 0 | pub fn to_str<'buf>(&self, buf: &'buf mut [u8]) -> Result<&'buf mut str, EncodeError> { |
204 | | #[allow(deprecated)] |
205 | 0 | let len = base32::encode_to(self.0, buf)?; |
206 | 0 | Ok(unsafe { core::str::from_utf8_unchecked_mut(&mut buf[..len]) }) |
207 | 0 | } |
208 | | |
209 | | /// Creates a Crockford Base32 encoded string that represents this Ulid |
210 | | /// |
211 | | /// # Example |
212 | | /// ```rust |
213 | | /// use ulid::Ulid; |
214 | | /// |
215 | | /// let text = "01D39ZY06FGSCTVN4T2V9PKHFZ"; |
216 | | /// let ulid = Ulid::from_string(text).unwrap(); |
217 | | /// |
218 | | /// let mut buf = [0; ulid::ULID_LEN]; |
219 | | /// let new_text = ulid.array_to_str(&mut buf); |
220 | | /// |
221 | | /// assert_eq!(new_text, text); |
222 | | /// ``` |
223 | 0 | pub fn array_to_str<'buf>(&self, buf: &'buf mut [u8; ULID_LEN]) -> &'buf mut str { |
224 | 0 | base32::encode_to_array(self.0, buf); |
225 | 0 | unsafe { core::str::from_utf8_unchecked_mut(buf) } |
226 | 0 | } |
227 | | |
228 | | /// Creates a Crockford Base32 encoded string that represents this Ulid |
229 | | /// |
230 | | /// # Example |
231 | | /// ```rust |
232 | | /// use ulid::Ulid; |
233 | | /// |
234 | | /// let text = "01D39ZY06FGSCTVN4T2V9PKHFZ"; |
235 | | /// let ulid = Ulid::from_string(text).unwrap(); |
236 | | /// |
237 | | /// assert_eq!(&ulid.to_string(), text); |
238 | | /// ``` |
239 | | #[allow(clippy::inherent_to_string_shadow_display)] // Significantly faster than Display::to_string |
240 | | #[cfg(feature = "std")] |
241 | 0 | pub fn to_string(&self) -> String { |
242 | 0 | base32::encode(self.0) |
243 | 0 | } |
244 | | |
245 | | /// Test if the Ulid is nil |
246 | | /// |
247 | | /// # Example |
248 | | /// ```rust |
249 | | /// use ulid::Ulid; |
250 | | /// |
251 | | /// # #[cfg(not(feature = "std"))] |
252 | | /// # let ulid = Ulid(1); |
253 | | /// # #[cfg(feature = "std")] |
254 | | /// let ulid = Ulid::new(); |
255 | | /// assert!(!ulid.is_nil()); |
256 | | /// |
257 | | /// let nil = Ulid::nil(); |
258 | | /// assert!(nil.is_nil()); |
259 | | /// ``` |
260 | 0 | pub const fn is_nil(&self) -> bool { |
261 | 0 | self.0 == 0u128 |
262 | 0 | } |
263 | | |
264 | | /// Increment the random number, make sure that the ts millis stays the same |
265 | 0 | pub const fn increment(&self) -> Option<Ulid> { |
266 | | const MAX_RANDOM: u128 = bitmask!(Ulid::RAND_BITS); |
267 | | |
268 | 0 | if (self.0 & MAX_RANDOM) == MAX_RANDOM { |
269 | 0 | None |
270 | | } else { |
271 | 0 | Some(Ulid(self.0 + 1)) |
272 | | } |
273 | 0 | } |
274 | | |
275 | | /// Creates a Ulid using the provided bytes array. |
276 | | /// |
277 | | /// # Example |
278 | | /// ``` |
279 | | /// use ulid::Ulid; |
280 | | /// let bytes = [0xFF; 16]; |
281 | | /// |
282 | | /// let ulid = Ulid::from_bytes(bytes); |
283 | | /// |
284 | | /// assert_eq!( |
285 | | /// ulid.to_string(), |
286 | | /// "7ZZZZZZZZZZZZZZZZZZZZZZZZZ" |
287 | | /// ); |
288 | | /// ``` |
289 | 0 | pub const fn from_bytes(bytes: [u8; 16]) -> Ulid { |
290 | 0 | Self(u128::from_be_bytes(bytes)) |
291 | 0 | } |
292 | | |
293 | | /// Returns the bytes of the Ulid in big-endian order. |
294 | | /// |
295 | | /// # Example |
296 | | /// ``` |
297 | | /// use ulid::Ulid; |
298 | | /// |
299 | | /// let text = "7ZZZZZZZZZZZZZZZZZZZZZZZZZ"; |
300 | | /// let ulid = Ulid::from_string(text).unwrap(); |
301 | | /// |
302 | | /// assert_eq!(ulid.to_bytes(), [0xFF; 16]); |
303 | | /// ``` |
304 | 0 | pub const fn to_bytes(&self) -> [u8; 16] { |
305 | 0 | self.0.to_be_bytes() |
306 | 0 | } |
307 | | } |
308 | | |
309 | | impl Default for Ulid { |
310 | 0 | fn default() -> Self { |
311 | 0 | Ulid::nil() |
312 | 0 | } |
313 | | } |
314 | | |
315 | | #[cfg(feature = "std")] |
316 | | impl From<Ulid> for String { |
317 | 0 | fn from(ulid: Ulid) -> String { |
318 | 0 | ulid.to_string() |
319 | 0 | } |
320 | | } |
321 | | |
322 | | impl From<(u64, u64)> for Ulid { |
323 | 0 | fn from((msb, lsb): (u64, u64)) -> Self { |
324 | 0 | Ulid(u128::from(msb) << 64 | u128::from(lsb)) |
325 | 0 | } |
326 | | } |
327 | | |
328 | | impl From<Ulid> for (u64, u64) { |
329 | 0 | fn from(ulid: Ulid) -> (u64, u64) { |
330 | 0 | ((ulid.0 >> 64) as u64, (ulid.0 & bitmask!(64)) as u64) |
331 | 0 | } |
332 | | } |
333 | | |
334 | | impl From<u128> for Ulid { |
335 | 0 | fn from(value: u128) -> Ulid { |
336 | 0 | Ulid(value) |
337 | 0 | } |
338 | | } |
339 | | |
340 | | impl From<Ulid> for u128 { |
341 | 0 | fn from(ulid: Ulid) -> u128 { |
342 | 0 | ulid.0 |
343 | 0 | } |
344 | | } |
345 | | |
346 | | impl From<[u8; 16]> for Ulid { |
347 | 0 | fn from(bytes: [u8; 16]) -> Self { |
348 | 0 | Self(u128::from_be_bytes(bytes)) |
349 | 0 | } |
350 | | } |
351 | | |
352 | | impl From<Ulid> for [u8; 16] { |
353 | 0 | fn from(ulid: Ulid) -> Self { |
354 | 0 | ulid.0.to_be_bytes() |
355 | 0 | } |
356 | | } |
357 | | |
358 | | impl FromStr for Ulid { |
359 | | type Err = DecodeError; |
360 | | |
361 | 0 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
362 | 0 | Ulid::from_string(s) |
363 | 0 | } |
364 | | } |
365 | | |
366 | | impl TryFrom<&'_ str> for Ulid { |
367 | | type Error = DecodeError; |
368 | | |
369 | 0 | fn try_from(value: &'_ str) -> Result<Self, Self::Error> { |
370 | 0 | Ulid::from_string(value) |
371 | 0 | } |
372 | | } |
373 | | |
374 | | impl fmt::Display for Ulid { |
375 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { |
376 | 0 | let mut buffer = [0; ULID_LEN]; |
377 | 0 | write!(f, "{}", self.array_to_str(&mut buffer)) |
378 | 0 | } |
379 | | } |
380 | | |
381 | | #[cfg(all(test, feature = "std"))] |
382 | | mod tests { |
383 | | use super::*; |
384 | | |
385 | | #[test] |
386 | | fn test_static() { |
387 | | let s = Ulid(0x41414141414141414141414141414141).to_string(); |
388 | | let u = Ulid::from_string(&s).unwrap(); |
389 | | assert_eq!(&s, "21850M2GA1850M2GA1850M2GA1"); |
390 | | assert_eq!(u.0, 0x41414141414141414141414141414141); |
391 | | } |
392 | | |
393 | | #[test] |
394 | | fn test_increment() { |
395 | | let ulid = Ulid::from_string("01BX5ZZKBKAZZZZZZZZZZZZZZZ").unwrap(); |
396 | | let ulid = ulid.increment().unwrap(); |
397 | | assert_eq!("01BX5ZZKBKB000000000000000", ulid.to_string()); |
398 | | |
399 | | let ulid = Ulid::from_string("01BX5ZZKBKZZZZZZZZZZZZZZZX").unwrap(); |
400 | | let ulid = ulid.increment().unwrap(); |
401 | | assert_eq!("01BX5ZZKBKZZZZZZZZZZZZZZZY", ulid.to_string()); |
402 | | let ulid = ulid.increment().unwrap(); |
403 | | assert_eq!("01BX5ZZKBKZZZZZZZZZZZZZZZZ", ulid.to_string()); |
404 | | assert!(ulid.increment().is_none()); |
405 | | } |
406 | | |
407 | | #[test] |
408 | | fn test_increment_overflow() { |
409 | | let ulid = Ulid(u128::max_value()); |
410 | | assert!(ulid.increment().is_none()); |
411 | | } |
412 | | |
413 | | #[test] |
414 | | fn can_into_thing() { |
415 | | let ulid = Ulid::from_str("01FKMG6GAG0PJANMWFN84TNXCD").unwrap(); |
416 | | let s: String = ulid.into(); |
417 | | let u: u128 = ulid.into(); |
418 | | let uu: (u64, u64) = ulid.into(); |
419 | | let bytes: [u8; 16] = ulid.into(); |
420 | | |
421 | | assert_eq!(Ulid::from_str(&s).unwrap(), ulid); |
422 | | assert_eq!(Ulid::from(u), ulid); |
423 | | assert_eq!(Ulid::from(uu), ulid); |
424 | | assert_eq!(Ulid::from(bytes), ulid); |
425 | | } |
426 | | |
427 | | #[test] |
428 | | fn default_is_nil() { |
429 | | assert_eq!(Ulid::default(), Ulid::nil()); |
430 | | } |
431 | | |
432 | | #[test] |
433 | | fn can_display_things() { |
434 | | println!("{}", Ulid::nil()); |
435 | | println!("{}", EncodeError::BufferTooSmall); |
436 | | println!("{}", DecodeError::InvalidLength); |
437 | | println!("{}", DecodeError::InvalidChar); |
438 | | } |
439 | | } |