1use serde::{Deserialize, Serialize};
2use sha2::{Digest, Sha256};
3use std::fmt;
4use uuid::Uuid;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
10pub struct TenantId(pub u32);
11
12impl TenantId {
13 pub const SYSTEM: Self = TenantId(0);
15 pub const SINGLE: Self = TenantId(1);
17 pub const FIRST_ASSIGNED: u32 = 2;
19 pub const UNREGISTERED: Self = TenantId(u32::MAX);
22
23 #[must_use]
25 pub fn single() -> Self {
26 Self::SINGLE
27 }
28
29 #[must_use]
35 pub const fn from_ordinal(ordinal: u64) -> Self {
36 assert!(ordinal <= u32::MAX as u64, "tenant ordinal exceeds u32");
37 #[expect(clippy::cast_possible_truncation, reason = "range checked above")]
38 TenantId(ordinal as u32)
39 }
40
41 #[must_use]
43 pub const fn to_be_bytes(self) -> [u8; 4] {
44 self.0.to_be_bytes()
45 }
46}
47
48impl fmt::Display for TenantId {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 write!(f, "{}", self.0)
51 }
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
58pub struct TenantScope(pub [u8; 32]);
59
60impl TenantScope {
61 #[must_use]
63 pub fn of(scope: &str) -> Self {
64 TenantScope(Sha256::digest(scope.as_bytes()).into())
65 }
66
67 #[must_use]
69 pub fn single() -> Self {
70 Self::of("rushdb:single-tenant")
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn arbitrary_scope_values_are_stable_and_distinct() {
80 assert_ne!(TenantScope::of("scope-a"), TenantScope::of("scope-b"));
81 assert_eq!(TenantScope::of("not-a-uuid"), TenantScope::of("not-a-uuid"));
82 }
83}
84
85macro_rules! dense_id {
86 ($name:ident) => {
87 #[doc = concat!("Dense monotonic ", stringify!($name), " identifier.")]
88 #[derive(
89 Debug,
90 Clone,
91 Copy,
92 PartialEq,
93 Eq,
94 PartialOrd,
95 Ord,
96 Hash,
97 Serialize,
98 Deserialize,
99 Default,
100 )]
101 pub struct $name(#[doc = "Wrapped ordinal value."] pub u64);
102
103 impl $name {
104 #[must_use]
106 pub fn next(self) -> Self {
107 $name(self.0 + 1)
108 }
109 }
110 };
111}
112
113dense_id!(RecordOrdinal);
114dense_id!(RelationshipOrdinal);
115dense_id!(OccurrenceOrdinal);
116dense_id!(FamilyId);
117dense_id!(PropertyId);
118dense_id!(ValueSeq);
119dense_id!(SpaceId);
120
121pub type TokenId = u32;
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
126pub struct RecordId(pub [u8; 16]);
127
128impl RecordId {
129 #[must_use]
131 pub fn generate() -> Self {
132 RecordId(*Uuid::now_v7().as_bytes())
133 }
134
135 #[must_use]
137 pub fn nil() -> Self {
138 RecordId([0u8; 16])
139 }
140
141 #[must_use]
143 pub fn to_uuid(&self) -> Uuid {
144 Uuid::from_bytes(self.0)
145 }
146}
147
148impl From<Uuid> for RecordId {
149 fn from(u: Uuid) -> Self {
150 RecordId(*u.as_bytes())
151 }
152}
153
154impl fmt::Display for RecordId {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 write!(f, "{}", self.to_uuid().simple())
157 }
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
162pub struct RelationshipId(pub [u8; 16]);
163
164impl RelationshipId {
165 #[must_use]
167 pub fn generate() -> Self {
168 RelationshipId(*Uuid::now_v7().as_bytes())
169 }
170
171 #[must_use]
173 pub fn to_uuid(&self) -> Uuid {
174 Uuid::from_bytes(self.0)
175 }
176}
177
178impl From<Uuid> for RelationshipId {
179 fn from(u: Uuid) -> Self {
180 RelationshipId(*u.as_bytes())
181 }
182}
183
184impl fmt::Display for RelationshipId {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 write!(f, "{}", self.to_uuid().simple())
187 }
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
192pub enum OwnerKind {
193 Record,
195 Relationship,
197}
198
199impl OwnerKind {
200 #[must_use]
202 pub fn as_byte(self) -> u8 {
203 match self {
204 OwnerKind::Record => b'r',
205 OwnerKind::Relationship => b'e',
206 }
207 }
208
209 #[must_use]
211 pub fn from_byte(b: u8) -> Option<Self> {
212 match b {
213 b'r' => Some(OwnerKind::Record),
214 b'e' => Some(OwnerKind::Relationship),
215 _ => None,
216 }
217 }
218}