1#![feature(decl_macro)]
2
3use std::{
4 fmt::Debug,
5 time::{SystemTime, UNIX_EPOCH},
6};
7
8use rand::Rng;
9use serde::{Deserialize, Deserializer, Serialize};
10
11pub const CROCKFORD_DIGITS: &[u8] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
12
13pub fn crockford_char_to_digit(c: char) -> Option<u8> {
14 return Some(match c.to_ascii_uppercase() {
15 '0' | 'O' => 0,
16 '1' | 'I' | 'L' => 1,
17 '2' => 2,
18 '3' => 3,
19 '4' => 4,
20 '5' => 5,
21 '6' => 6,
22 '7' => 7,
23 '8' => 8,
24 '9' => 9,
25 'A' => 10,
26 'B' => 11,
27 'C' => 12,
28 'D' => 13,
29 'E' => 14,
30 'F' => 15,
31 'G' => 16,
32 'H' => 17,
33 'J' => 18,
34 'K' => 19,
35 'M' => 20,
36 'N' => 21,
37 'P' => 22,
38 'Q' => 23,
39 'R' => 24,
40 'S' => 25,
41 'T' => 26,
42 'V' => 27,
43 'W' => 28,
44 'X' => 29,
45 'Y' => 30,
46 'Z' => 31,
47 _ => return None,
48 });
49}
50
51pub trait IntoCrockford {
52 fn into_crockford(&self, padding: usize) -> String;
53}
54
55pub trait FromCrockford {
56 fn from_crockford(value: &str) -> Option<Self>
57 where
58 Self: Sized;
59}
60
61macro impl_crockford($t:ty) {
62 impl IntoCrockford for $t {
63 fn into_crockford(&self, padding: usize) -> String {
64 if *self == 0 {
65 return "0".repeat(padding.max(1));
66 }
67
68 let mut value = *self;
69 let mut result = Vec::new();
70
71 while value > 0 {
72 let rem = (value % 32) as usize;
73 result.push(CROCKFORD_DIGITS[rem] as char);
74 value >>= 5;
75 }
76
77 let mut encoded: String = result.iter().rev().collect();
78 if encoded.len() < padding {
79 let pad_len = padding - encoded.len();
80 encoded = "0".repeat(pad_len) + &encoded;
81 }
82
83 encoded
84 }
85 }
86
87 impl FromCrockford for $t {
88 fn from_crockford(value: &str) -> Option<Self> {
89 let mut result: $t = 0;
90
91 for c in value.chars() {
92 let digit = crockford_char_to_digit(c)?;
93 result = result.checked_mul(32)?.checked_add(digit as $t)?;
94 }
95
96 Some(result)
97 }
98 }
99}
100
101impl_crockford!(u8);
102impl_crockford!(u16);
103impl_crockford!(u32);
104impl_crockford!(u64);
105impl_crockford!(u128);
106
107#[repr(C)]
108#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
109pub struct Ulid([u8; 16]);
110
111impl Ulid {
112 pub fn from_raw_parts(timestamp: u128, random: u128) -> Self {
113 let timestamp = timestamp.to_be_bytes();
114 let random = random.to_be_bytes();
115
116 let mut bytes = [0u8; 16];
117
118 bytes[..6].copy_from_slice(×tamp[10..]);
119 bytes[6..].copy_from_slice(&random[6..]);
120
121 return Self(bytes);
122 }
123
124 pub fn into_raw_parts(&self) -> (u128, u128) {
125 let mut timestamp_bytes = [0u8; 16];
126 timestamp_bytes[10..].copy_from_slice(self.timestamp_bytes());
127
128 let mut random_bytes = [0u8; 16];
129 random_bytes[6..].copy_from_slice(self.random_bytes());
130
131 let timestamp = u128::from_be_bytes(timestamp_bytes);
132 let random = u128::from_be_bytes(random_bytes);
133
134 return (timestamp, random);
135 }
136
137 pub fn new() -> Self {
138 let timestamp = SystemTime::now()
139 .duration_since(UNIX_EPOCH)
140 .unwrap()
141 .as_millis();
142
143 let timestamp = timestamp.to_be_bytes();
144
145 let mut bytes = [0u8; 16];
146
147 bytes[..6].copy_from_slice(×tamp[10..]);
148
149 rand::rng().fill_bytes(&mut bytes[6..]);
150
151 return Self(bytes);
152 }
153
154 pub fn timestamp_bytes(&self) -> &[u8] {
155 return &self.0[..6];
156 }
157
158 pub fn random_bytes(&self) -> &[u8] {
159 return &self.0[6..];
160 }
161}
162
163impl Debug for Ulid {
164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 return f
166 .debug_tuple("Ulid")
167 .field(&self.into_crockford(26))
168 .finish();
169 }
170}
171
172impl From<Ulid> for u128 {
173 fn from(value: Ulid) -> Self {
174 return Self::from_be_bytes(value.0);
175 }
176}
177
178impl From<u128> for Ulid {
179 fn from(value: u128) -> Self {
180 return Self(value.to_be_bytes());
181 }
182}
183
184impl From<Ulid> for [u8; 16] {
185 fn from(value: Ulid) -> [u8; 16] {
186 return value.0;
187 }
188}
189
190impl From<[u8; 16]> for Ulid {
191 fn from(bytes: [u8; 16]) -> Ulid {
192 return Ulid(bytes);
193 }
194}
195
196impl IntoCrockford for Ulid {
197 fn into_crockford(&self, _: usize) -> String {
198 let (timestamp, random) = self.into_raw_parts();
199
200 return timestamp.into_crockford(10) + &random.into_crockford(16);
201 }
202}
203
204impl FromCrockford for Ulid {
205 fn from_crockford(value: &str) -> Option<Self>
206 where
207 Self: Sized,
208 {
209 let (timestamp, random) = value.split_at_checked(10)?;
210
211 let timestamp = u128::from_crockford(timestamp)?;
212 let random = u128::from_crockford(random)?;
213
214 return Some(Self::from_raw_parts(timestamp, random));
215 }
216}
217
218impl Serialize for Ulid {
219 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
220 where
221 S: serde::Serializer,
222 {
223 if serializer.is_human_readable() {
224 return self.into_crockford(0).serialize(serializer);
225 }
226
227 return self.into_raw_parts().serialize(serializer);
228 }
229}
230
231impl<'de> Deserialize<'de> for Ulid {
232 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
233 where
234 D: Deserializer<'de>,
235 {
236 if deserializer.is_human_readable() {
237 let value = String::deserialize(deserializer)?;
238
239 return Ulid::from_crockford(&value)
240 .ok_or_else(|| serde::de::Error::custom("invalid ULID string"));
241 }
242
243 let (timestamp, random) = <(u128, u128)>::deserialize(deserializer)?;
244
245 return Ok(Ulid::from_raw_parts(timestamp, random));
246 }
247}