1use core::fmt::{self, Debug};
38use core::hash::Hash;
39use core::marker::PhantomData;
40use core::str::FromStr;
41
42pub use ulid::Ulid;
43
44#[derive(Clone, Copy)]
49pub struct Id<K> {
50 inner: Ulid,
51 _phantom: PhantomData<K>,
52}
53
54impl<K> Debug for Id<K> {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 f.debug_struct("Id").field("inner", &self.inner).finish()
59 }
60}
61
62impl<K> PartialEq for Id<K> {
63 fn eq(&self, other: &Self) -> bool {
64 self.inner == other.inner
65 }
66}
67
68impl<K> Eq for Id<K> {}
69
70impl<K> Hash for Id<K> {
71 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
72 self.inner.hash(state)
73 }
74}
75
76impl<K> PartialOrd for Id<K> {
77 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
78 Some(self.cmp(other))
79 }
80}
81
82impl<K> Ord for Id<K> {
83 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
84 self.inner.cmp(&other.inner)
85 }
86}
87
88impl<K> Id<K> {
89 #[cfg(feature = "gen")]
94 #[inline]
95 pub fn new() -> Self {
96 Self::from_ulid(new_ulid())
97 }
98
99 #[inline]
101 pub fn from_ulid(ulid: Ulid) -> Self {
102 Self {
103 inner: ulid,
104 _phantom: PhantomData,
105 }
106 }
107
108 #[inline]
110 pub fn as_ulid(&self) -> Ulid {
111 self.inner
112 }
113
114 #[inline]
116 pub fn ulid(self) -> Ulid {
117 self.inner
118 }
119
120 #[inline]
122 pub fn to_bytes(&self) -> [u8; 16] {
123 self.inner.to_bytes()
124 }
125
126 #[inline]
128 pub fn from_bytes(bytes: [u8; 16]) -> Self {
129 Self {
130 inner: Ulid::from_bytes(bytes),
131 _phantom: PhantomData,
132 }
133 }
134
135 #[inline]
137 pub fn timestamp_ms(&self) -> u64 {
138 self.inner.timestamp_ms()
139 }
140}
141
142impl<K> Default for Id<K> {
143 #[cfg(feature = "gen")]
144 #[inline]
145 fn default() -> Self {
146 Self::new()
147 }
148
149 #[cfg(not(feature = "gen"))]
150 #[inline]
151 fn default() -> Self {
152 panic!("Id::default() requires the 'gen' feature to be enabled")
153 }
154}
155
156impl<K> fmt::Display for Id<K> {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 fmt::Display::fmt(&self.inner, f)
159 }
160}
161
162impl<K> FromStr for Id<K> {
163 type Err = ulid::DecodeError;
164
165 fn from_str(s: &str) -> Result<Self, Self::Err> {
166 Ok(Self {
167 inner: Ulid::from_str(s)?,
168 _phantom: PhantomData,
169 })
170 }
171}
172
173#[cfg(feature = "serde")]
174impl<K> serde::Serialize for Id<K> {
175 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
176 where
177 S: serde::Serializer,
178 {
179 let s = self.to_string();
180 serializer.serialize_str(&s)
181 }
182}
183
184#[cfg(feature = "serde")]
185impl<'de, K> serde::Deserialize<'de> for Id<K> {
186 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
187 where
188 D: serde::Deserializer<'de>,
189 {
190 use std::borrow::Cow;
191
192 let s: Cow<'de, str> = Cow::deserialize(deserializer)?;
193 let ulid = Ulid::from_str(&s).map_err(serde::de::Error::custom)?;
194 Ok(Self::from_ulid(ulid))
195 }
196}
197
198#[cfg(feature = "gen")]
204#[inline]
205pub fn new_ulid() -> Ulid {
206 fn now() -> std::time::SystemTime {
207 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
208 {
209 use web_time::web::SystemTimeExt;
210 return web_time::SystemTime::now().to_std();
211 }
212 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
213 return std::time::SystemTime::now();
214 }
215
216 fn random_u128_80() -> u128 {
217 let mut bytes = [0u8; 10];
218 getrandom::getrandom(&mut bytes).expect("getrandom failed");
219
220 let mut buf = [0u8; 16];
221 buf[6..].copy_from_slice(&bytes);
222 u128::from_be_bytes(buf)
223 }
224
225 let timestamp_ms = now()
226 .duration_since(std::time::SystemTime::UNIX_EPOCH)
227 .unwrap_or(std::time::Duration::ZERO)
228 .as_millis() as u64;
229
230 Ulid::from_parts(timestamp_ms, random_u128_80())
231}
232
233#[cfg(feature = "gen")]
237#[inline]
238pub fn new_ulid_string() -> String {
239 new_ulid().to_string()
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 struct User;
248
249 type UserId = Id<User>;
250
251 #[cfg(feature = "gen")]
252 struct Order;
253 #[cfg(feature = "gen")]
254 type OrderId = Id<Order>;
255
256 #[cfg(feature = "gen")]
257 #[test]
258 fn test_id_generation() {
259 let id1 = UserId::new();
260 let id2 = UserId::new();
261 assert_ne!(id1, id2);
262 }
263
264 #[cfg(feature = "gen")]
265 #[test]
266 fn test_type_safety() {
267 let user_id = UserId::new();
268 let order_id = OrderId::new();
269
270 assert_ne!(user_id.as_ulid(), order_id.as_ulid());
276 }
277
278 #[test]
279 fn test_string_roundtrip() {
280 let ulid = Ulid::from_string("01ARZ3NDEKTSV4RRFFQ69G5FAV").unwrap();
281 let id = UserId::from_ulid(ulid);
282 let s = id.to_string();
283 let id2 = UserId::from_str(&s).unwrap();
284 assert_eq!(id, id2);
285 }
286
287 #[test]
288 fn test_bytes_roundtrip() {
289 let ulid = Ulid::from_string("01ARZ3NDEKTSV4RRFFQ69G5FAV").unwrap();
290 let id = UserId::from_ulid(ulid);
291 let bytes = id.to_bytes();
292 let id2 = UserId::from_bytes(bytes);
293 assert_eq!(id, id2);
294 }
295
296 #[cfg(feature = "serde")]
297 #[test]
298 fn test_serde_roundtrip() {
299 let ulid = Ulid::from_string("01ARZ3NDEKTSV4RRFFQ69G5FAV").unwrap();
300 let id = UserId::from_ulid(ulid);
301 let json = serde_json::to_string(&id).unwrap();
302 let id2: UserId = serde_json::from_str(&json).unwrap();
303 assert_eq!(id, id2);
304 }
305}