Skip to main content

reifydb_type/value/identity/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::{fmt, ops::Deref, str::FromStr};
5
6use reifydb_runtime::context::{clock::Clock, rng::Rng};
7use serde::{Deserialize, Deserializer, Serialize, Serializer, de, de::Visitor};
8use uuid::Uuid;
9
10use crate::value::uuid::Uuid7;
11
12#[repr(transparent)]
13#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash, Default)]
14pub struct IdentityId(pub Uuid7);
15
16impl IdentityId {
17	pub fn generate(clock: &Clock, rng: &Rng) -> Self {
18		IdentityId(Uuid7::generate(clock, rng))
19	}
20
21	pub fn new(id: Uuid7) -> Self {
22		IdentityId(id)
23	}
24
25	pub fn value(&self) -> Uuid7 {
26		self.0
27	}
28
29	pub fn anonymous() -> Self {
30		let bytes = [
31			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32		];
33		IdentityId(Uuid7(Uuid::from_bytes(bytes)))
34	}
35
36	pub fn root() -> Self {
37		let bytes = [
38			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
39		];
40		IdentityId(Uuid7(Uuid::from_bytes(bytes)))
41	}
42
43	pub fn system() -> Self {
44		let bytes = [
45			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
46		];
47		IdentityId(Uuid7(Uuid::from_bytes(bytes)))
48	}
49
50	pub fn is_anonymous(&self) -> bool {
51		*self == Self::anonymous()
52	}
53
54	pub fn is_root(&self) -> bool {
55		*self == Self::root()
56	}
57
58	pub fn is_system(&self) -> bool {
59		*self == Self::system()
60	}
61
62	pub fn is_privileged(&self) -> bool {
63		self.is_root() || self.is_system()
64	}
65}
66
67impl Deref for IdentityId {
68	type Target = Uuid7;
69
70	fn deref(&self) -> &Self::Target {
71		&self.0
72	}
73}
74
75impl PartialEq<Uuid7> for IdentityId {
76	fn eq(&self, other: &Uuid7) -> bool {
77		self.0.eq(other)
78	}
79}
80
81impl From<Uuid7> for IdentityId {
82	fn from(id: Uuid7) -> Self {
83		IdentityId(id)
84	}
85}
86
87impl From<IdentityId> for Uuid7 {
88	fn from(identity_id: IdentityId) -> Self {
89		identity_id.0
90	}
91}
92
93impl fmt::Display for IdentityId {
94	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95		write!(f, "{}", self.0)
96	}
97}
98
99impl Serialize for IdentityId {
100	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
101	where
102		S: Serializer,
103	{
104		self.0.serialize(serializer)
105	}
106}
107
108impl<'de> Deserialize<'de> for IdentityId {
109	fn deserialize<D>(deserializer: D) -> Result<IdentityId, D::Error>
110	where
111		D: Deserializer<'de>,
112	{
113		struct Uuid7Visitor;
114
115		impl<'de> Visitor<'de> for Uuid7Visitor {
116			type Value = IdentityId;
117
118			fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
119				formatter.write_str("a UUID version 7")
120			}
121
122			fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
123			where
124				E: de::Error,
125			{
126				let uuid =
127					Uuid::from_str(value).map_err(|e| E::custom(format!("invalid UUID: {}", e)))?;
128
129				if uuid.get_version_num() != 7 {
130					return Err(E::custom(format!(
131						"expected UUID v7, got v{}",
132						uuid.get_version_num()
133					)));
134				}
135
136				Ok(IdentityId(Uuid7::from(uuid)))
137			}
138
139			fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
140			where
141				E: de::Error,
142			{
143				let uuid = Uuid::from_slice(value)
144					.map_err(|e| E::custom(format!("invalid UUID bytes: {}", e)))?;
145
146				if uuid.get_version_num() != 7 {
147					return Err(E::custom(format!(
148						"expected UUID v7, got v{}",
149						uuid.get_version_num()
150					)));
151				}
152
153				Ok(IdentityId(Uuid7::from(uuid)))
154			}
155		}
156
157		if deserializer.is_human_readable() {
158			deserializer.deserialize_str(Uuid7Visitor)
159		} else {
160			deserializer.deserialize_bytes(Uuid7Visitor)
161		}
162	}
163}
164
165#[cfg(test)]
166pub mod tests {
167	use postcard::{from_bytes, to_allocvec};
168	use reifydb_runtime::context::clock::MockClock;
169	use serde_json::{from_str, to_string};
170
171	use super::*;
172
173	fn test_clock_and_rng() -> (MockClock, Clock, Rng) {
174		let mock = MockClock::from_millis(1000);
175		let clock = Clock::Mock(mock.clone());
176		let rng = Rng::seeded(42);
177		(mock, clock, rng)
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}