1use std::{fmt, ops::Deref, str::FromStr};
5
6use serde::{Deserialize, Deserializer, Serialize, Serializer, de, de::Visitor};
7use uuid::Uuid;
8
9use crate::{
10 clock::{ClockNow, RandomBytes},
11 value::uuid::Uuid7,
12};
13
14#[repr(transparent)]
15#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash, Default)]
16pub struct IdentityId(pub Uuid7);
17
18impl IdentityId {
19 pub fn generate<C: ClockNow, R: RandomBytes>(clock: &C, rng: &R) -> Self {
20 IdentityId(Uuid7::generate(clock, rng))
21 }
22
23 pub fn new(id: Uuid7) -> Self {
24 IdentityId(id)
25 }
26
27 pub fn value(&self) -> Uuid7 {
28 self.0
29 }
30
31 pub fn anonymous() -> Self {
32 let bytes = [
33 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
34 ];
35 IdentityId(Uuid7(Uuid::from_bytes(bytes)))
36 }
37
38 pub fn root() -> Self {
39 let bytes = [
40 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
41 ];
42 IdentityId(Uuid7(Uuid::from_bytes(bytes)))
43 }
44
45 pub fn system() -> Self {
46 let bytes = [
47 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
48 ];
49 IdentityId(Uuid7(Uuid::from_bytes(bytes)))
50 }
51
52 pub fn is_anonymous(&self) -> bool {
53 *self == Self::anonymous()
54 }
55
56 pub fn is_root(&self) -> bool {
57 *self == Self::root()
58 }
59
60 pub fn is_system(&self) -> bool {
61 *self == Self::system()
62 }
63
64 pub fn sentinel_kind(&self) -> Option<IdentityKind> {
65 if self.is_root() {
66 Some(IdentityKind::Root)
67 } else if self.is_system() {
68 Some(IdentityKind::System)
69 } else if self.is_anonymous() {
70 Some(IdentityKind::Anonymous)
71 } else {
72 None
73 }
74 }
75
76 pub fn is_privileged(&self) -> bool {
77 matches!(self.sentinel_kind(), Some(IdentityKind::Root | IdentityKind::System))
78 }
79}
80
81#[repr(u8)]
82#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
83pub enum IdentityKind {
84 User = 0,
85 Service = 1,
86 Root = 2,
87 System = 3,
88 Anonymous = 4,
89 Guest = 5,
90}
91
92impl IdentityKind {
93 pub fn to_u8(self) -> u8 {
94 match self {
95 IdentityKind::User => 0,
96 IdentityKind::Service => 1,
97 IdentityKind::Root => 2,
98 IdentityKind::System => 3,
99 IdentityKind::Anonymous => 4,
100 IdentityKind::Guest => 5,
101 }
102 }
103
104 pub fn from_u8(value: u8) -> Self {
105 match value {
106 0 => IdentityKind::User,
107 1 => IdentityKind::Service,
108 2 => IdentityKind::Root,
109 3 => IdentityKind::System,
110 4 => IdentityKind::Anonymous,
111 5 => IdentityKind::Guest,
112 _ => IdentityKind::User,
113 }
114 }
115
116 pub fn as_str(self) -> &'static str {
117 match self {
118 IdentityKind::User => "user",
119 IdentityKind::Service => "service",
120 IdentityKind::Root => "root",
121 IdentityKind::System => "system",
122 IdentityKind::Anonymous => "anonymous",
123 IdentityKind::Guest => "guest",
124 }
125 }
126
127 pub fn is_builtin(self) -> bool {
128 matches!(self, IdentityKind::Root | IdentityKind::System | IdentityKind::Anonymous)
129 }
130}
131
132impl fmt::Display for IdentityKind {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 f.write_str(self.as_str())
135 }
136}
137
138impl Deref for IdentityId {
139 type Target = Uuid7;
140
141 fn deref(&self) -> &Self::Target {
142 &self.0
143 }
144}
145
146impl PartialEq<Uuid7> for IdentityId {
147 fn eq(&self, other: &Uuid7) -> bool {
148 self.0.eq(other)
149 }
150}
151
152impl From<Uuid7> for IdentityId {
153 fn from(id: Uuid7) -> Self {
154 IdentityId(id)
155 }
156}
157
158impl From<IdentityId> for Uuid7 {
159 fn from(identity_id: IdentityId) -> Self {
160 identity_id.0
161 }
162}
163
164impl fmt::Display for IdentityId {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 write!(f, "{}", self.0)
167 }
168}
169
170impl Serialize for IdentityId {
171 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
172 where
173 S: Serializer,
174 {
175 Serialize::serialize(&self.0, serializer)
176 }
177}
178
179impl<'de> Deserialize<'de> for IdentityId {
180 fn deserialize<D>(deserializer: D) -> Result<IdentityId, D::Error>
181 where
182 D: Deserializer<'de>,
183 {
184 struct Uuid7Visitor;
185
186 impl<'de> Visitor<'de> for Uuid7Visitor {
187 type Value = IdentityId;
188
189 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
190 formatter.write_str("a UUID version 7")
191 }
192
193 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
194 where
195 E: de::Error,
196 {
197 let uuid =
198 Uuid::from_str(value).map_err(|e| E::custom(format!("invalid UUID: {}", e)))?;
199
200 if uuid.get_version_num() != 7 {
201 return Err(E::custom(format!(
202 "expected UUID v7, got v{}",
203 uuid.get_version_num()
204 )));
205 }
206
207 Ok(IdentityId(Uuid7::from(uuid)))
208 }
209
210 fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
211 where
212 E: de::Error,
213 {
214 let uuid = Uuid::from_slice(value)
215 .map_err(|e| E::custom(format!("invalid UUID bytes: {}", e)))?;
216
217 if uuid.get_version_num() != 7 {
218 return Err(E::custom(format!(
219 "expected UUID v7, got v{}",
220 uuid.get_version_num()
221 )));
222 }
223
224 Ok(IdentityId(Uuid7::from(uuid)))
225 }
226 }
227
228 if deserializer.is_human_readable() {
229 deserializer.deserialize_str(Uuid7Visitor)
230 } else {
231 deserializer.deserialize_bytes(Uuid7Visitor)
232 }
233 }
234}
235
236#[cfg(test)]
237pub mod tests {
238 use postcard::{from_bytes, to_allocvec};
239 use serde_json::{from_str, to_string};
240
241 use super::*;
242 use crate::clock::testing::{TestClock, TestRng};
243
244 fn test_clock_and_rng() -> (TestClock, TestClock, TestRng) {
245 let clock = TestClock::from_millis(1000);
246 (clock.clone(), clock, TestRng)
247 }
248
249 #[test]
250 fn test_identity_id_creation() {
251 let (_, clock, rng) = test_clock_and_rng();
252 let id = IdentityId::generate(&clock, &rng);
253 assert_ne!(id, IdentityId::default());
254 }
255
256 #[test]
257 fn test_identity_id_from_uuid7() {
258 let (_, clock, rng) = test_clock_and_rng();
259 let uuid = Uuid7::generate(&clock, &rng);
260 let id = IdentityId::from(uuid);
261 assert_eq!(id.value(), uuid);
262 }
263
264 #[test]
265 fn test_identity_id_display() {
266 let (_, clock, rng) = test_clock_and_rng();
267 let id = IdentityId::generate(&clock, &rng);
268 let display = format!("{}", id);
269 assert!(!display.is_empty());
270 }
271
272 #[test]
273 fn test_identity_id_equality() {
274 let (_, clock, rng) = test_clock_and_rng();
275 let uuid = Uuid7::generate(&clock, &rng);
276 let id1 = IdentityId::from(uuid);
277 let id2 = IdentityId::from(uuid);
278 assert_eq!(id1, id2);
279 }
280
281 #[test]
282 fn test_identity_id_postcard_roundtrip() {
283 let (_, clock, rng) = test_clock_and_rng();
284 let id = IdentityId::generate(&clock, &rng);
285 let bytes = to_allocvec(&id).expect("postcard serialize");
286 let decoded: IdentityId = from_bytes(&bytes).expect("postcard deserialize");
287 assert_eq!(id, decoded);
288 }
289
290 #[test]
291 fn test_identity_id_postcard_roundtrip_root() {
292 let id = IdentityId::root();
293 let bytes = to_allocvec(&id).expect("postcard serialize root");
294 let decoded: IdentityId = from_bytes(&bytes).expect("postcard deserialize root");
295 assert_eq!(id, decoded);
296 }
297
298 #[test]
299 fn test_identity_id_json_roundtrip() {
300 let (_, clock, rng) = test_clock_and_rng();
301 let id = IdentityId::generate(&clock, &rng);
302 let s = to_string(&id).expect("json serialize");
303 let decoded: IdentityId = from_str(&s).expect("json deserialize");
304 assert_eq!(id, decoded);
305 }
306
307 #[test]
308 fn test_sentinel_kind_covers_all_three_sentinels() {
309 assert_eq!(IdentityId::root().sentinel_kind(), Some(IdentityKind::Root));
314 assert_eq!(IdentityId::system().sentinel_kind(), Some(IdentityKind::System));
315 assert_eq!(IdentityId::anonymous().sentinel_kind(), Some(IdentityKind::Anonymous));
316 }
317
318 #[test]
319 fn test_sentinel_kind_is_none_for_a_regular_identity() {
320 let (_, clock, rng) = test_clock_and_rng();
323 assert_eq!(IdentityId::generate(&clock, &rng).sentinel_kind(), None);
324 }
325
326 #[test]
327 fn test_default_identity_id_is_not_anonymous() {
328 assert_ne!(IdentityId::default(), IdentityId::anonymous());
333 assert_eq!(IdentityId::default().sentinel_kind(), None);
334 }
335
336 #[test]
337 fn test_is_privileged_is_root_and_system_only() {
338 assert!(IdentityId::root().is_privileged());
341 assert!(IdentityId::system().is_privileged());
342 assert!(!IdentityId::anonymous().is_privileged());
343 let (_, clock, rng) = test_clock_and_rng();
344 assert!(!IdentityId::generate(&clock, &rng).is_privileged());
345 }
346
347 #[test]
348 fn test_identity_kind_u8_roundtrip() {
349 for kind in [
352 IdentityKind::User,
353 IdentityKind::Service,
354 IdentityKind::Root,
355 IdentityKind::System,
356 IdentityKind::Anonymous,
357 IdentityKind::Guest,
358 ] {
359 assert_eq!(IdentityKind::from_u8(kind.to_u8()), kind);
360 }
361 }
362
363 #[test]
364 fn test_identity_kind_discriminants_are_stable() {
365 assert_eq!(IdentityKind::User.to_u8(), 0);
369 assert_eq!(IdentityKind::Service.to_u8(), 1);
370 assert_eq!(IdentityKind::Root.to_u8(), 2);
371 assert_eq!(IdentityKind::System.to_u8(), 3);
372 assert_eq!(IdentityKind::Anonymous.to_u8(), 4);
373 assert_eq!(IdentityKind::Guest.to_u8(), 5);
374 }
375
376 #[test]
377 fn test_identity_kind_names_are_distinct() {
378 let names = [
382 IdentityKind::User.as_str(),
383 IdentityKind::Service.as_str(),
384 IdentityKind::Root.as_str(),
385 IdentityKind::System.as_str(),
386 IdentityKind::Anonymous.as_str(),
387 IdentityKind::Guest.as_str(),
388 ];
389 for (i, a) in names.iter().enumerate() {
390 for b in &names[i + 1..] {
391 assert_ne!(a, b);
392 }
393 }
394 assert_eq!(IdentityKind::Guest.as_str(), "guest");
395 }
396
397 #[test]
398 fn test_identity_kind_user_is_zero() {
399 assert_eq!(IdentityKind::User.to_u8(), 0);
402 }
403
404 #[test]
405 fn test_identity_kind_from_unknown_u8_falls_back_to_user() {
406 let kind = IdentityKind::from_u8(200);
410 assert_eq!(kind, IdentityKind::User);
411 assert!(!kind.is_builtin());
412 }
413
414 #[test]
415 fn test_is_builtin_matches_the_unstorable_kinds() {
416 assert!(IdentityKind::Root.is_builtin());
419 assert!(IdentityKind::System.is_builtin());
420 assert!(IdentityKind::Anonymous.is_builtin());
421 assert!(!IdentityKind::User.is_builtin());
422 assert!(!IdentityKind::Service.is_builtin());
423 assert!(!IdentityKind::Guest.is_builtin());
426 }
427}