1use anyhow::{Result, anyhow};
5use multibase::Base;
6use serde::{Deserialize, Serialize};
7use std::fmt;
8use std::str::FromStr;
9
10#[derive(Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
18pub struct Vid(u64);
19
20impl Vid {
21 pub fn new(id: u64) -> Self {
23 Self(id)
24 }
25
26 pub fn as_u64(&self) -> u64 {
28 self.0
29 }
30
31 pub const INVALID: Vid = Vid(u64::MAX);
33
34 pub fn is_invalid(&self) -> bool {
36 self.0 == u64::MAX
37 }
38}
39
40impl From<u64> for Vid {
41 fn from(val: u64) -> Self {
42 Self(val)
43 }
44}
45
46impl From<Vid> for u64 {
47 fn from(vid: Vid) -> Self {
48 vid.0
49 }
50}
51
52impl Default for Vid {
53 fn default() -> Self {
54 Self::INVALID
55 }
56}
57
58impl fmt::Debug for Vid {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 if self.is_invalid() {
61 write!(f, "Vid(INVALID)")
62 } else {
63 write!(f, "Vid({})", self.0)
64 }
65 }
66}
67
68impl fmt::Display for Vid {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 write!(f, "{}", self.0)
71 }
72}
73
74impl FromStr for Vid {
75 type Err = anyhow::Error;
76
77 fn from_str(s: &str) -> Result<Self> {
79 let id: u64 = s
80 .parse()
81 .map_err(|e| anyhow!("Invalid Vid '{}': {}", s, e))?;
82 Ok(Self::new(id))
83 }
84}
85
86#[derive(Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
92pub struct Eid(u64);
93
94impl Eid {
95 pub fn new(id: u64) -> Self {
97 Self(id)
98 }
99
100 pub fn as_u64(&self) -> u64 {
102 self.0
103 }
104
105 pub const INVALID: Eid = Eid(u64::MAX);
107
108 pub fn is_invalid(&self) -> bool {
110 self.0 == u64::MAX
111 }
112}
113
114impl From<u64> for Eid {
115 fn from(val: u64) -> Self {
116 Self(val)
117 }
118}
119
120impl From<Eid> for u64 {
121 fn from(eid: Eid) -> Self {
122 eid.0
123 }
124}
125
126impl Default for Eid {
127 fn default() -> Self {
128 Self::INVALID
129 }
130}
131
132impl fmt::Debug for Eid {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 if self.is_invalid() {
135 write!(f, "Eid(INVALID)")
136 } else {
137 write!(f, "Eid({})", self.0)
138 }
139 }
140}
141
142impl fmt::Display for Eid {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 write!(f, "{}", self.0)
145 }
146}
147
148impl FromStr for Eid {
149 type Err = anyhow::Error;
150
151 fn from_str(s: &str) -> Result<Self> {
153 let id: u64 = s
154 .parse()
155 .map_err(|e| anyhow!("Invalid Eid '{}': {}", s, e))?;
156 Ok(Self::new(id))
157 }
158}
159
160#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
166pub struct DenseIdx(pub u32);
167
168impl DenseIdx {
169 pub fn new(idx: u32) -> Self {
171 Self(idx)
172 }
173
174 pub fn as_usize(&self) -> usize {
176 self.0 as usize
177 }
178
179 pub fn as_u32(&self) -> u32 {
181 self.0
182 }
183
184 pub const INVALID: DenseIdx = DenseIdx(u32::MAX);
186
187 pub fn is_invalid(&self) -> bool {
189 self.0 == u32::MAX
190 }
191}
192
193impl From<u32> for DenseIdx {
194 fn from(val: u32) -> Self {
195 Self(val)
196 }
197}
198
199impl From<usize> for DenseIdx {
200 fn from(val: usize) -> Self {
201 Self(val as u32)
202 }
203}
204
205impl fmt::Display for DenseIdx {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 write!(f, "{}", self.0)
208 }
209}
210
211#[derive(Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
213pub struct UniId([u8; 32]);
214
215impl UniId {
216 pub fn from_bytes(bytes: [u8; 32]) -> Self {
217 Self(bytes)
218 }
219
220 pub fn from_multibase(s: &str) -> Result<Self> {
235 let (base, bytes) =
236 multibase::decode(s).map_err(|e| anyhow!("Multibase decode error: {}", e))?;
237
238 if base != Base::Base32Lower {
240 return Err(anyhow!(
241 "UniId must use Base32Lower encoding, got {:?}",
242 base
243 ));
244 }
245
246 let inner: [u8; 32] = bytes.try_into().map_err(|v: Vec<u8>| {
247 anyhow!("Invalid UniId length: expected 32 bytes, got {}", v.len())
248 })?;
249
250 Ok(Self(inner))
251 }
252
253 pub fn to_multibase(&self) -> String {
254 multibase::encode(Base::Base32Lower, self.0)
255 }
256
257 pub fn as_bytes(&self) -> &[u8; 32] {
258 &self.0
259 }
260}
261
262impl fmt::Debug for UniId {
263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264 write!(f, "UniId({})", self.to_multibase())
265 }
266}
267
268impl fmt::Display for UniId {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 write!(f, "{}", self.to_multibase())
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
279 fn test_vid_basic() {
280 let vid = Vid::new(12345);
281 assert_eq!(vid.as_u64(), 12345);
282 assert!(!vid.is_invalid());
283 }
284
285 #[test]
286 fn test_vid_invalid() {
287 let vid = Vid::INVALID;
288 assert!(vid.is_invalid());
289 assert_eq!(vid.as_u64(), u64::MAX);
290 }
291
292 #[test]
293 fn test_vid_from_str() {
294 let vid: Vid = "42".parse().unwrap();
295 assert_eq!(vid.as_u64(), 42);
296
297 let original = Vid::new(12345678);
299 let s = original.to_string();
300 let parsed: Vid = s.parse().unwrap();
301 assert_eq!(original, parsed);
302
303 assert!("invalid".parse::<Vid>().is_err());
305 assert!("".parse::<Vid>().is_err());
306 }
307
308 #[test]
309 fn test_eid_basic() {
310 let eid = Eid::new(67890);
311 assert_eq!(eid.as_u64(), 67890);
312 assert!(!eid.is_invalid());
313 }
314
315 #[test]
316 fn test_eid_invalid() {
317 let eid = Eid::INVALID;
318 assert!(eid.is_invalid());
319 assert_eq!(eid.as_u64(), u64::MAX);
320 }
321
322 #[test]
323 fn test_eid_from_str() {
324 let eid: Eid = "100".parse().unwrap();
325 assert_eq!(eid.as_u64(), 100);
326
327 let original = Eid::new(0xABCDEF);
329 let s = original.to_string();
330 let parsed: Eid = s.parse().unwrap();
331 assert_eq!(original, parsed);
332
333 assert!("invalid".parse::<Eid>().is_err());
335 }
336
337 #[test]
338 fn test_dense_idx() {
339 let idx = DenseIdx::new(100);
340 assert_eq!(idx.as_usize(), 100);
341 assert_eq!(idx.as_u32(), 100);
342 assert!(!idx.is_invalid());
343
344 let invalid = DenseIdx::INVALID;
345 assert!(invalid.is_invalid());
346 }
347
348 #[test]
349 fn test_uni_id_multibase() {
350 let bytes = [0u8; 32];
351 let uid = UniId(bytes);
352 let s = uid.to_multibase();
353 let decoded = UniId::from_multibase(&s).unwrap();
354 assert_eq!(uid, decoded);
355 }
356
357 mod security_tests {
359 use super::*;
360
361 #[test]
363 fn test_uni_id_rejects_wrong_encoding() {
364 let bytes = [0u8; 32];
366 let base58_encoded = multibase::encode(multibase::Base::Base58Btc, bytes);
367
368 let result = UniId::from_multibase(&base58_encoded);
369 assert!(result.is_err());
370 assert!(
371 result
372 .unwrap_err()
373 .to_string()
374 .contains("Base32Lower encoding")
375 );
376 }
377
378 #[test]
380 fn test_uni_id_rejects_wrong_length() {
381 let short_bytes = [0u8; 16];
383 let encoded = multibase::encode(Base::Base32Lower, short_bytes);
384
385 let result = UniId::from_multibase(&encoded);
386 assert!(result.is_err());
387 assert!(
388 result
389 .unwrap_err()
390 .to_string()
391 .contains("expected 32 bytes")
392 );
393 }
394 }
395}