Skip to main content

reifydb_value/value/identity/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use 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 is_privileged(&self) -> bool {
65		self.is_root() || self.is_system()
66	}
67}
68
69impl Deref for IdentityId {
70	type Target = Uuid7;
71
72	fn deref(&self) -> &Self::Target {
73		&self.0
74	}
75}
76
77impl PartialEq<Uuid7> for IdentityId {
78	fn eq(&self, other: &Uuid7) -> bool {
79		self.0.eq(other)
80	}
81}
82
83impl From<Uuid7> for IdentityId {
84	fn from(id: Uuid7) -> Self {
85		IdentityId(id)
86	}
87}
88
89impl From<IdentityId> for Uuid7 {
90	fn from(identity_id: IdentityId) -> Self {
91		identity_id.0
92	}
93}
94
95impl fmt::Display for IdentityId {
96	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97		write!(f, "{}", self.0)
98	}
99}
100
101impl Serialize for IdentityId {
102	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
103	where
104		S: Serializer,
105	{
106		Serialize::serialize(&self.0, serializer)
107	}
108}
109
110impl<'de> Deserialize<'de> for IdentityId {
111	fn deserialize<D>(deserializer: D) -> Result<IdentityId, D::Error>
112	where
113		D: Deserializer<'de>,
114	{
115		struct Uuid7Visitor;
116
117		impl<'de> Visitor<'de> for Uuid7Visitor {
118			type Value = IdentityId;
119
120			fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
121				formatter.write_str("a UUID version 7")
122			}
123
124			fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
125			where
126				E: de::Error,
127			{
128				let uuid =
129					Uuid::from_str(value).map_err(|e| E::custom(format!("invalid UUID: {}", e)))?;
130
131				if uuid.get_version_num() != 7 {
132					return Err(E::custom(format!(
133						"expected UUID v7, got v{}",
134						uuid.get_version_num()
135					)));
136				}
137
138				Ok(IdentityId(Uuid7::from(uuid)))
139			}
140
141			fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
142			where
143				E: de::Error,
144			{
145				let uuid = Uuid::from_slice(value)
146					.map_err(|e| E::custom(format!("invalid UUID bytes: {}", e)))?;
147
148				if uuid.get_version_num() != 7 {
149					return Err(E::custom(format!(
150						"expected UUID v7, got v{}",
151						uuid.get_version_num()
152					)));
153				}
154
155				Ok(IdentityId(Uuid7::from(uuid)))
156			}
157		}
158
159		if deserializer.is_human_readable() {
160			deserializer.deserialize_str(Uuid7Visitor)
161		} else {
162			deserializer.deserialize_bytes(Uuid7Visitor)
163		}
164	}
165}
166
167#[cfg(test)]
168pub mod tests {
169	use postcard::{from_bytes, to_allocvec};
170	use serde_json::{from_str, to_string};
171
172	use super::*;
173	use crate::clock::testing::{TestClock, TestRng};
174
175	fn test_clock_and_rng() -> (TestClock, TestClock, TestRng) {
176		let clock = TestClock::from_millis(1000);
177		(clock.clone(), clock, TestRng)
178	}
179
180	#[test]
181	fn test_identity_id_creation() {
182		let (_, clock, rng) = test_clock_and_rng();
183		let id = IdentityId::generate(&clock, &rng);
184		assert_ne!(id, IdentityId::default());
185	}
186
187	#[test]
188	fn test_identity_id_from_uuid7() {
189		let (_, clock, rng) = test_clock_and_rng();
190		let uuid = Uuid7::generate(&clock, &rng);
191		let id = IdentityId::from(uuid);
192		assert_eq!(id.value(), uuid);
193	}
194
195	#[test]
196	fn test_identity_id_display() {
197		let (_, clock, rng) = test_clock_and_rng();
198		let id = IdentityId::generate(&clock, &rng);
199		let display = format!("{}", id);
200		assert!(!display.is_empty());
201	}
202
203	#[test]
204	fn test_identity_id_equality() {
205		let (_, clock, rng) = test_clock_and_rng();
206		let uuid = Uuid7::generate(&clock, &rng);
207		let id1 = IdentityId::from(uuid);
208		let id2 = IdentityId::from(uuid);
209		assert_eq!(id1, id2);
210	}
211
212	#[test]
213	fn test_identity_id_postcard_roundtrip() {
214		let (_, clock, rng) = test_clock_and_rng();
215		let id = IdentityId::generate(&clock, &rng);
216		let bytes = to_allocvec(&id).expect("postcard serialize");
217		let decoded: IdentityId = from_bytes(&bytes).expect("postcard deserialize");
218		assert_eq!(id, decoded);
219	}
220
221	#[test]
222	fn test_identity_id_postcard_roundtrip_root() {
223		let id = IdentityId::root();
224		let bytes = to_allocvec(&id).expect("postcard serialize root");
225		let decoded: IdentityId = from_bytes(&bytes).expect("postcard deserialize root");
226		assert_eq!(id, decoded);
227	}
228
229	#[test]
230	fn test_identity_id_json_roundtrip() {
231		let (_, clock, rng) = test_clock_and_rng();
232		let id = IdentityId::generate(&clock, &rng);
233		let s = to_string(&id).expect("json serialize");
234		let decoded: IdentityId = from_str(&s).expect("json deserialize");
235		assert_eq!(id, decoded);
236	}
237}