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/// An identity identifier - a unique UUID v7 for an identity
13#[repr(transparent)]
14#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash, Default)]
15pub struct IdentityId(pub Uuid7);
16
17impl IdentityId {
18	/// Create a new IdentityId with a generated UUID v7 using the provided clock and RNG.
19	pub fn generate(clock: &Clock, rng: &Rng) -> Self {
20		IdentityId(Uuid7::generate(clock, rng))
21	}
22
23	/// Create a new IdentityId from an existing Uuid7
24	pub fn new(id: Uuid7) -> Self {
25		IdentityId(id)
26	}
27
28	/// Get the inner Uuid7 value
29	pub fn value(&self) -> Uuid7 {
30		self.0
31	}
32
33	/// Sentinel for anonymous identity: minimum valid UUID v7
34	/// `00000000-0000-7000-8000-000000000000`
35	pub fn anonymous() -> Self {
36		let bytes = [
37			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
38		];
39		IdentityId(Uuid7(Uuid::from_bytes(bytes)))
40	}
41
42	/// Sentinel for root identity: maximum valid UUID v7
43	/// `ffffffff-ffff-7fff-bfff-ffffffffffff`
44	pub fn root() -> Self {
45		let bytes = [
46			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
47		];
48		IdentityId(Uuid7(Uuid::from_bytes(bytes)))
49	}
50
51	/// Sentinel for system identity: used for internal engine-initiated operations.
52	/// `ffffffff-fffe-7fff-bfff-ffffffffffff`
53	pub fn system() -> Self {
54		let bytes = [
55			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
56		];
57		IdentityId(Uuid7(Uuid::from_bytes(bytes)))
58	}
59
60	pub fn is_anonymous(&self) -> bool {
61		*self == Self::anonymous()
62	}
63
64	pub fn is_root(&self) -> bool {
65		*self == Self::root()
66	}
67
68	pub fn is_system(&self) -> bool {
69		*self == Self::system()
70	}
71
72	/// Returns true for any privileged identity that bypasses policy enforcement.
73	pub fn is_privileged(&self) -> bool {
74		self.is_root() || self.is_system()
75	}
76}
77
78impl Deref for IdentityId {
79	type Target = Uuid7;
80
81	fn deref(&self) -> &Self::Target {
82		&self.0
83	}
84}
85
86impl PartialEq<Uuid7> for IdentityId {
87	fn eq(&self, other: &Uuid7) -> bool {
88		self.0.eq(other)
89	}
90}
91
92impl From<Uuid7> for IdentityId {
93	fn from(id: Uuid7) -> Self {
94		IdentityId(id)
95	}
96}
97
98impl From<IdentityId> for Uuid7 {
99	fn from(identity_id: IdentityId) -> Self {
100		identity_id.0
101	}
102}
103
104impl fmt::Display for IdentityId {
105	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106		write!(f, "{}", self.0)
107	}
108}
109
110impl Serialize for IdentityId {
111	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
112	where
113		S: Serializer,
114	{
115		self.0.serialize(serializer)
116	}
117}
118
119impl<'de> Deserialize<'de> for IdentityId {
120	fn deserialize<D>(deserializer: D) -> Result<IdentityId, D::Error>
121	where
122		D: Deserializer<'de>,
123	{
124		struct Uuid7Visitor;
125
126		impl<'de> Visitor<'de> for Uuid7Visitor {
127			type Value = IdentityId;
128
129			fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
130				formatter.write_str("a UUID version 7")
131			}
132
133			fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
134			where
135				E: de::Error,
136			{
137				let uuid =
138					Uuid::from_str(value).map_err(|e| E::custom(format!("invalid UUID: {}", e)))?;
139
140				if uuid.get_version_num() != 7 {
141					return Err(E::custom(format!(
142						"expected UUID v7, got v{}",
143						uuid.get_version_num()
144					)));
145				}
146
147				Ok(IdentityId(Uuid7::from(uuid)))
148			}
149
150			fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
151			where
152				E: de::Error,
153			{
154				let uuid = Uuid::from_slice(value)
155					.map_err(|e| E::custom(format!("invalid UUID bytes: {}", e)))?;
156
157				// Verify it's a v7 UUID or nil
158				if uuid.get_version_num() != 7 {
159					return Err(E::custom(format!(
160						"expected UUID v7, got v{}",
161						uuid.get_version_num()
162					)));
163				}
164
165				Ok(IdentityId(Uuid7::from(uuid)))
166			}
167		}
168
169		deserializer.deserialize_any(Uuid7Visitor)
170	}
171}
172
173#[cfg(test)]
174pub mod tests {
175	use reifydb_runtime::context::clock::MockClock;
176
177	use super::*;
178
179	fn test_clock_and_rng() -> (MockClock, Clock, Rng) {
180		let mock = MockClock::from_millis(1000);
181		let clock = Clock::Mock(mock.clone());
182		let rng = Rng::seeded(42);
183		(mock, clock, rng)
184	}
185
186	#[test]
187	fn test_identity_id_creation() {
188		let (_, clock, rng) = test_clock_and_rng();
189		let id = IdentityId::generate(&clock, &rng);
190		assert_ne!(id, IdentityId::default());
191	}
192
193	#[test]
194	fn test_identity_id_from_uuid7() {
195		let (_, clock, rng) = test_clock_and_rng();
196		let uuid = Uuid7::generate(&clock, &rng);
197		let id = IdentityId::from(uuid);
198		assert_eq!(id.value(), uuid);
199	}
200
201	#[test]
202	fn test_identity_id_display() {
203		let (_, clock, rng) = test_clock_and_rng();
204		let id = IdentityId::generate(&clock, &rng);
205		let display = format!("{}", id);
206		assert!(!display.is_empty());
207	}
208
209	#[test]
210	fn test_identity_id_equality() {
211		let (_, clock, rng) = test_clock_and_rng();
212		let uuid = Uuid7::generate(&clock, &rng);
213		let id1 = IdentityId::from(uuid);
214		let id2 = IdentityId::from(uuid);
215		assert_eq!(id1, id2);
216	}
217}