1use std::cell::RefCell;
4use std::collections::HashMap;
5use std::fmt::{self, Display};
6use std::hash::Hash;
7use std::mem::transmute;
8use std::str::FromStr;
9use std::time::{Duration, UNIX_EPOCH};
10
11use serde::{Deserialize, Serialize};
12use ulid::Ulid;
13use uuid::{Uuid, Version};
14
15use crate::error::IdentifierError;
16
17pub trait Identifier:
22 Sized + Clone + PartialEq + Eq + Hash + Display + Serialize + for<'de> Deserialize<'de>
23{
24 fn parse<S: AsRef<str>>(s: S) -> Result<Self, IdentifierError>;
26
27 fn generate() -> Self;
29
30 fn as_str(&self) -> &str;
32
33 fn timestamp_ms(&self) -> Option<u64>;
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
39pub struct UuidIdentifier(Uuid);
40
41thread_local! {
43 static UUID_CACHE: RefCell<HashMap<Uuid, String>> =
44 RefCell::new(HashMap::new());
45}
46
47impl UuidIdentifier {
48 pub fn new_v4() -> Self {
50 Self(Uuid::new_v4())
51 }
52
53 pub fn new_v5(namespace: &Uuid, name: &str) -> Self {
55 Self(Uuid::new_v5(namespace, name.as_bytes()))
56 }
57
58 pub fn uuid(&self) -> Uuid {
60 self.0
61 }
62
63 pub fn version(&self) -> Option<Version> {
65 self.0.get_version()
66 }
67}
68
69impl Identifier for UuidIdentifier {
70 fn parse<S: AsRef<str>>(s: S) -> Result<Self, IdentifierError> {
71 Ok(Self(
72 Uuid::parse_str(s.as_ref()).map_err(IdentifierError::from)?,
73 ))
74 }
75
76 fn generate() -> Self {
77 Self::new_v4()
78 }
79
80 fn as_str(&self) -> &str {
81 UUID_CACHE.with(|cache| {
82 let mut cache = cache.borrow_mut();
83 cache.entry(self.0).or_insert_with(|| self.0.to_string());
84 unsafe { transmute(cache.get(&self.0).unwrap().as_str()) }
87 })
88 }
89
90 fn timestamp_ms(&self) -> Option<u64> {
91 None
94 }
95}
96
97impl Display for UuidIdentifier {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 write!(f, "{}", self.as_str())
100 }
101}
102
103impl From<Uuid> for UuidIdentifier {
104 fn from(uuid: Uuid) -> Self {
105 Self(uuid)
106 }
107}
108
109impl FromStr for UuidIdentifier {
110 type Err = IdentifierError;
111 fn from_str(s: &str) -> Result<Self, Self::Err> {
112 Self::parse(s)
113 }
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
118pub struct UlidIdentifier(Ulid);
119
120thread_local! {
122 static ULID_CACHE: RefCell<HashMap<Ulid, String>> =
123 RefCell::new(HashMap::new());
124}
125
126impl Default for UlidIdentifier {
127 fn default() -> Self {
128 Self::new()
129 }
130}
131
132impl UlidIdentifier {
133 pub fn new() -> Self {
135 Self(Ulid::new())
136 }
137
138 pub fn with_timestamp(timestamp_ms: u64) -> Self {
140 Self(Ulid::from_datetime(
142 UNIX_EPOCH + Duration::from_millis(timestamp_ms),
143 ))
144 }
145
146 pub fn ulid(&self) -> Ulid {
148 self.0
149 }
150
151 pub fn get_timestamp_ms(&self) -> u64 {
153 let datetime = self.0.datetime();
155 let since_epoch = datetime
156 .duration_since(UNIX_EPOCH)
157 .unwrap_or(Duration::from_secs(0));
158 since_epoch.as_millis() as u64
159 }
160
161 pub fn monotonic_from(previous: Option<&Self>) -> Self {
163 match previous {
164 Some(prev) => {
165 let new_ulid = Ulid::new();
166 let prev_ms = prev.get_timestamp_ms();
167 let new_ms = {
168 let datetime = new_ulid.datetime();
169 let since_epoch = datetime
170 .duration_since(UNIX_EPOCH)
171 .unwrap_or(Duration::from_secs(0));
172 since_epoch.as_millis() as u64
173 };
174
175 if new_ms <= prev_ms {
176 Self(Ulid::from_datetime(
178 UNIX_EPOCH + Duration::from_millis(prev_ms + 1),
179 ))
180 } else {
181 Self(new_ulid)
182 }
183 }
184 None => Self::new(),
185 }
186 }
187}
188
189impl Identifier for UlidIdentifier {
190 fn parse<S: AsRef<str>>(s: S) -> Result<Self, IdentifierError> {
191 Ok(Self(
192 Ulid::from_string(s.as_ref()).map_err(IdentifierError::from)?,
193 ))
194 }
195
196 fn generate() -> Self {
197 Self::new()
198 }
199
200 fn as_str(&self) -> &str {
201 ULID_CACHE.with(|cache| {
202 let mut cache = cache.borrow_mut();
203 cache.entry(self.0).or_insert_with(|| self.0.to_string());
204 unsafe { transmute(cache.get(&self.0).unwrap().as_str()) }
207 })
208 }
209
210 fn timestamp_ms(&self) -> Option<u64> {
211 Some(self.get_timestamp_ms())
212 }
213}
214
215impl Display for UlidIdentifier {
216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217 f.write_str(self.as_str())
218 }
219}
220
221impl From<Ulid> for UlidIdentifier {
222 fn from(ulid: Ulid) -> Self {
223 Self(ulid)
224 }
225}
226
227impl FromStr for UlidIdentifier {
228 type Err = IdentifierError;
229 fn from_str(s: &str) -> Result<Self, Self::Err> {
230 Self::parse(s)
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237
238 #[test]
239 fn test_uuid_identifier() {
240 let id = UuidIdentifier::generate();
241 let id_str = id.as_str();
242 let parsed = UuidIdentifier::parse(id_str).unwrap();
243 assert_eq!(id, parsed);
244 }
245
246 #[test]
247 fn test_ulid_identifier() {
248 let id = UlidIdentifier::generate();
249 let id_str = id.as_str();
250 let parsed = UlidIdentifier::parse(id_str).unwrap();
251 assert_eq!(id, parsed);
252
253 assert!(id.timestamp_ms().is_some());
255 }
256}