| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
use rand::RngCore; |
| 6 |
|
| 7 |
const CROCKFORD: &[u8; 32] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ"; |
| 8 |
|
| 9 |
|
| 10 |
pub fn ulid() -> String { |
| 11 |
let mut r = [0u8; 16]; |
| 12 |
rand::rngs::OsRng.fill_bytes(&mut r); |
| 13 |
ulid_from(now_ms(), u128::from_be_bytes(r)) |
| 14 |
} |
| 15 |
|
| 16 |
|
| 17 |
pub fn ulid_from(ms: u64, random: u128) -> String { |
| 18 |
let v: u128 = ((ms as u128 & ((1 << 48) - 1)) << 80) | (random & ((1u128 << 80) - 1)); |
| 19 |
(0..26).map(|i| CROCKFORD[((v >> (125 - 5 * i)) & 31) as usize] as char).collect() |
| 20 |
} |
| 21 |
|
| 22 |
|
| 23 |
pub fn now_ms() -> u64 { |
| 24 |
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_millis() as u64).unwrap_or(0) |
| 25 |
} |
| 26 |
|
| 27 |
#[cfg(test)] |
| 28 |
mod tests { |
| 29 |
use super::*; |
| 30 |
|
| 31 |
#[test] |
| 32 |
fn ulids_are_26_chars_and_sort_by_time() { |
| 33 |
let a = ulid_from(1_000, 5); |
| 34 |
let b = ulid_from(2_000, 0); |
| 35 |
assert_eq!(a.len(), 26); |
| 36 |
assert!(a < b); |
| 37 |
assert_eq!(ulid_from(0, 0), "00000000000000000000000000"); |
| 38 |
assert_ne!(ulid(), ulid()); |
| 39 |
} |
| 40 |
} |