reifydb-type 0.4.11

Core type system and value representations for ReifyDB
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2025 ReifyDB

use std::{fmt, ops::Deref, str::FromStr};

use reifydb_runtime::context::{clock::Clock, rng::Rng};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de, de::Visitor};
use uuid::Uuid;

use crate::value::uuid::Uuid7;

/// An identity identifier - a unique UUID v7 for an identity
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash, Default)]
pub struct IdentityId(pub Uuid7);

impl IdentityId {
	/// Create a new IdentityId with a generated UUID v7 using the provided clock and RNG.
	pub fn generate(clock: &Clock, rng: &Rng) -> Self {
		IdentityId(Uuid7::generate(clock, rng))
	}

	/// Create a new IdentityId from an existing Uuid7
	pub fn new(id: Uuid7) -> Self {
		IdentityId(id)
	}

	/// Get the inner Uuid7 value
	pub fn value(&self) -> Uuid7 {
		self.0
	}

	/// Sentinel for anonymous identity: minimum valid UUID v7
	/// `00000000-0000-7000-8000-000000000000`
	pub fn anonymous() -> Self {
		let bytes = [
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		];
		IdentityId(Uuid7(Uuid::from_bytes(bytes)))
	}

	/// Sentinel for root identity: maximum valid UUID v7
	/// `ffffffff-ffff-7fff-bfff-ffffffffffff`
	pub fn root() -> Self {
		let bytes = [
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		];
		IdentityId(Uuid7(Uuid::from_bytes(bytes)))
	}

	/// Sentinel for system identity: used for internal engine-initiated operations.
	/// `ffffffff-fffe-7fff-bfff-ffffffffffff`
	pub fn system() -> Self {
		let bytes = [
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		];
		IdentityId(Uuid7(Uuid::from_bytes(bytes)))
	}

	pub fn is_anonymous(&self) -> bool {
		*self == Self::anonymous()
	}

	pub fn is_root(&self) -> bool {
		*self == Self::root()
	}

	pub fn is_system(&self) -> bool {
		*self == Self::system()
	}

	/// Returns true for any privileged identity that bypasses policy enforcement.
	pub fn is_privileged(&self) -> bool {
		self.is_root() || self.is_system()
	}
}

impl Deref for IdentityId {
	type Target = Uuid7;

	fn deref(&self) -> &Self::Target {
		&self.0
	}
}

impl PartialEq<Uuid7> for IdentityId {
	fn eq(&self, other: &Uuid7) -> bool {
		self.0.eq(other)
	}
}

impl From<Uuid7> for IdentityId {
	fn from(id: Uuid7) -> Self {
		IdentityId(id)
	}
}

impl From<IdentityId> for Uuid7 {
	fn from(identity_id: IdentityId) -> Self {
		identity_id.0
	}
}

impl fmt::Display for IdentityId {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		write!(f, "{}", self.0)
	}
}

impl Serialize for IdentityId {
	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
	where
		S: Serializer,
	{
		self.0.serialize(serializer)
	}
}

impl<'de> Deserialize<'de> for IdentityId {
	fn deserialize<D>(deserializer: D) -> Result<IdentityId, D::Error>
	where
		D: Deserializer<'de>,
	{
		struct Uuid7Visitor;

		impl<'de> Visitor<'de> for Uuid7Visitor {
			type Value = IdentityId;

			fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
				formatter.write_str("a UUID version 7")
			}

			fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
			where
				E: de::Error,
			{
				let uuid =
					Uuid::from_str(value).map_err(|e| E::custom(format!("invalid UUID: {}", e)))?;

				if uuid.get_version_num() != 7 {
					return Err(E::custom(format!(
						"expected UUID v7, got v{}",
						uuid.get_version_num()
					)));
				}

				Ok(IdentityId(Uuid7::from(uuid)))
			}

			fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
			where
				E: de::Error,
			{
				let uuid = Uuid::from_slice(value)
					.map_err(|e| E::custom(format!("invalid UUID bytes: {}", e)))?;

				// Verify it's a v7 UUID or nil
				if uuid.get_version_num() != 7 {
					return Err(E::custom(format!(
						"expected UUID v7, got v{}",
						uuid.get_version_num()
					)));
				}

				Ok(IdentityId(Uuid7::from(uuid)))
			}
		}

		deserializer.deserialize_any(Uuid7Visitor)
	}
}

#[cfg(test)]
pub mod tests {
	use reifydb_runtime::context::clock::MockClock;

	use super::*;

	fn test_clock_and_rng() -> (MockClock, Clock, Rng) {
		let mock = MockClock::from_millis(1000);
		let clock = Clock::Mock(mock.clone());
		let rng = Rng::seeded(42);
		(mock, clock, rng)
	}

	#[test]
	fn test_identity_id_creation() {
		let (_, clock, rng) = test_clock_and_rng();
		let id = IdentityId::generate(&clock, &rng);
		assert_ne!(id, IdentityId::default());
	}

	#[test]
	fn test_identity_id_from_uuid7() {
		let (_, clock, rng) = test_clock_and_rng();
		let uuid = Uuid7::generate(&clock, &rng);
		let id = IdentityId::from(uuid);
		assert_eq!(id.value(), uuid);
	}

	#[test]
	fn test_identity_id_display() {
		let (_, clock, rng) = test_clock_and_rng();
		let id = IdentityId::generate(&clock, &rng);
		let display = format!("{}", id);
		assert!(!display.is_empty());
	}

	#[test]
	fn test_identity_id_equality() {
		let (_, clock, rng) = test_clock_and_rng();
		let uuid = Uuid7::generate(&clock, &rng);
		let id1 = IdentityId::from(uuid);
		let id2 = IdentityId::from(uuid);
		assert_eq!(id1, id2);
	}
}