Skip to main content

mongreldb_types/
ids.rs

1//! Common cluster-wide identifiers (spec section 7).
2//!
3//! Two families of identifiers are defined once here:
4//!
5//! - Random 128-bit IDs ([`ClusterId`], [`NodeId`], [`DatabaseId`],
6//!   [`TabletId`], [`RaftGroupId`], [`TransactionId`], [`QueryId`]). They are
7//!   drawn from the operating-system CSPRNG via [`getrandom`], are never
8//!   derived only from timestamps, and are never reused within a database.
9//! - Numeric 64-bit IDs ([`TableId`], [`SchemaVersion`], [`MetadataVersion`])
10//!   allocated through replicated catalog state; they are likewise never
11//!   reused within a database.
12//!
13//! # Text form
14//!
15//! The canonical text form of a 128-bit ID is strict lowercase hexadecimal,
16//! exactly 32 characters. Parsing is deliberately lenient: the hyphenated
17//! UUID form (`8-4-4-4-12`) and uppercase hex digits are also accepted, and
18//! hyphens are in fact ignored at any position. Only the canonical form is
19//! ever emitted, so persist and compare canonical output rather than
20//! user-supplied spellings.
21//!
22//! # Serde form
23//!
24//! For human-readable serializers (`Serializer::is_human_readable`, e.g.
25//! JSON) a 128-bit ID serializes as its canonical hex string and
26//! deserializes from any accepted string spelling. For binary serializers
27//! (e.g. bincode) the ID keeps the compact transparent `[u8; 16]` newtype
28//! form, byte-identical to the derived representation. Numeric IDs serialize
29//! as plain `u64` in both worlds.
30
31use core::fmt;
32use core::str::FromStr;
33
34/// Error returned when parsing a textual identifier fails.
35#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
36pub enum IdParseError {
37    /// The text had the wrong number of characters.
38    #[error("invalid id length: expected 32 hex digits or UUID form, got {0} chars")]
39    InvalidLength(usize),
40    /// The text contained a non-hexadecimal character.
41    #[error("invalid hex character `{0}` in id")]
42    InvalidCharacter(char),
43}
44
45fn hex_encode(bytes: &[u8; 16]) -> String {
46    const HEX: &[u8; 16] = b"0123456789abcdef";
47    let mut out = String::with_capacity(32);
48    for b in bytes {
49        out.push(HEX[(b >> 4) as usize] as char);
50        out.push(HEX[(b & 0x0f) as usize] as char);
51    }
52    out
53}
54
55fn hex_decode(text: &str) -> Result<[u8; 16], IdParseError> {
56    let len = text.chars().count();
57    if len != 32 {
58        return Err(IdParseError::InvalidLength(len));
59    }
60    let mut out = [0u8; 16];
61    let mut chars = text.chars();
62    for byte in &mut out {
63        let hi = chars.next().expect("length checked above");
64        let lo = chars.next().expect("length checked above");
65        let hi = hi.to_digit(16).ok_or(IdParseError::InvalidCharacter(hi))?;
66        let lo = lo.to_digit(16).ok_or(IdParseError::InvalidCharacter(lo))?;
67        *byte = ((hi << 4) | lo) as u8;
68    }
69    Ok(out)
70}
71
72macro_rules! id128 {
73    ($(#[$meta:meta])* $name:ident) => {
74        $(#[$meta])*
75        #[doc = ""]
76        #[doc = "Random 128-bit identifier drawn from the operating-system CSPRNG by"]
77        #[doc = "[`new_random`](Self::new_random); never derived only from timestamps and"]
78        #[doc = "never reused within a database. The all-zero value is reserved."]
79        #[doc = ""]
80        #[doc = "Canonical text form: strict lowercase hexadecimal, 32 characters."]
81        #[doc = "Parsing is lenient and additionally accepts the hyphenated UUID form"]
82        #[doc = "(`8-4-4-4-12`) and uppercase hex digits."]
83        #[repr(transparent)]
84        #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
85        pub struct $name(pub [u8; 16]);
86
87        impl $name {
88            /// The all-zero identifier (reserved; never produced by
89            /// [`Self::new_random`] in practice).
90            pub const ZERO: Self = Self([0u8; 16]);
91
92            /// Draws a fresh random identifier from the operating-system
93            /// CSPRNG. Never derived only from timestamps; with 128 random
94            /// bits the result is never reused within a database.
95            pub fn new_random() -> Self {
96                let mut bytes = [0u8; 16];
97                getrandom::getrandom(&mut bytes)
98                    .expect("operating-system CSPRNG unavailable");
99                Self(bytes)
100            }
101
102            /// Wraps raw bytes without copying.
103            pub const fn from_bytes(bytes: [u8; 16]) -> Self {
104                Self(bytes)
105            }
106
107            /// Borrows the raw 16 bytes.
108            pub const fn as_bytes(&self) -> &[u8; 16] {
109                &self.0
110            }
111
112            /// Canonical text form: strict lowercase hexadecimal (32 chars).
113            pub fn to_hex(self) -> String {
114                hex_encode(&self.0)
115            }
116        }
117
118        impl serde::Serialize for $name {
119            /// Human-readable serializers (e.g. JSON) receive the canonical
120            /// hex string; binary serializers receive the compact 16-byte
121            /// newtype form, byte-identical to a derived implementation.
122            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
123            where
124                S: serde::Serializer,
125            {
126                if serializer.is_human_readable() {
127                    serializer.serialize_str(&hex_encode(&self.0))
128                } else {
129                    serializer.serialize_newtype_struct(stringify!($name), &self.0)
130                }
131            }
132        }
133
134        impl<'de> serde::Deserialize<'de> for $name {
135            /// Human-readable deserializers accept any string spelling
136            /// accepted by [`FromStr`]; binary deserializers expect the
137            /// compact 16-byte newtype form.
138            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
139            where
140                D: serde::Deserializer<'de>,
141            {
142                struct HexVisitor;
143
144                impl serde::de::Visitor<'_> for HexVisitor {
145                    type Value = $name;
146
147                    fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148                        f.write_str(concat!(
149                            "a ",
150                            stringify!($name),
151                            " as 32 hex digits or hyphenated UUID form"
152                        ))
153                    }
154
155                    fn visit_str<E>(self, text: &str) -> Result<Self::Value, E>
156                    where
157                        E: serde::de::Error,
158                    {
159                        text.parse().map_err(serde::de::Error::custom)
160                    }
161                }
162
163                struct BytesVisitor;
164
165                impl<'de> serde::de::Visitor<'de> for BytesVisitor {
166                    type Value = $name;
167
168                    fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169                        f.write_str(concat!("a ", stringify!($name), " as 16 raw bytes"))
170                    }
171
172                    fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
173                    where
174                        D: serde::Deserializer<'de>,
175                    {
176                        <[u8; 16] as serde::Deserialize>::deserialize(deserializer).map($name)
177                    }
178
179                    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
180                    where
181                        A: serde::de::SeqAccess<'de>,
182                    {
183                        let bytes: [u8; 16] = match seq.next_element()? {
184                            Some(value) => value,
185                            None => return Err(serde::de::Error::invalid_length(0, &self)),
186                        };
187                        Ok($name(bytes))
188                    }
189                }
190
191                if deserializer.is_human_readable() {
192                    deserializer.deserialize_str(HexVisitor)
193                } else {
194                    deserializer.deserialize_newtype_struct(stringify!($name), BytesVisitor)
195                }
196            }
197        }
198
199        impl fmt::Display for $name {
200            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201                f.write_str(&hex_encode(&self.0))
202            }
203        }
204
205        impl fmt::Debug for $name {
206            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207                write!(f, concat!(stringify!($name), "({})"), hex_encode(&self.0))
208            }
209        }
210
211        impl FromStr for $name {
212            type Err = IdParseError;
213
214            /// Parses the canonical 32-character hex form. Lenient by
215            /// contract: hyphens are ignored at any position (so the
216            /// hyphenated UUID form `8-4-4-4-12` parses) and uppercase hex
217            /// digits are accepted. The canonical lowercase form emitted by
218            /// [`Display`](fmt::Display) and [`to_hex`](Self::to_hex) is the
219            /// only stable spelling for persistence.
220            fn from_str(text: &str) -> Result<Self, Self::Err> {
221                let compact: String = text.chars().filter(|c| *c != '-').collect();
222                hex_decode(&compact).map(Self)
223            }
224        }
225    };
226}
227
228id128!(
229    /// Identifies one MongrelDB cluster.
230    ClusterId
231);
232id128!(
233    /// Identifies one running server node within a cluster.
234    NodeId
235);
236id128!(
237    /// Identifies one logical database.
238    DatabaseId
239);
240id128!(
241    /// Identifies one independently replicated and movable data partition.
242    TabletId
243);
244id128!(
245    /// Identifies one consensus group.
246    RaftGroupId
247);
248id128!(
249    /// Identifies one transaction.
250    TransactionId
251);
252id128!(
253    /// Identifies one query execution.
254    QueryId
255);
256
257macro_rules! id64 {
258    ($(#[$meta:meta])* $name:ident) => {
259        $(#[$meta])*
260        #[doc = ""]
261        #[doc = "Numeric 64-bit identifier allocated through replicated catalog state;"]
262        #[doc = "never reused within a database. The zero value is reserved."]
263        #[repr(transparent)]
264        #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
265        pub struct $name(pub u64);
266
267        impl $name {
268            /// The zero value (reserved).
269            pub const ZERO: Self = Self(0);
270
271            /// Wraps a raw value.
272            pub const fn new(value: u64) -> Self {
273                Self(value)
274            }
275
276            /// Returns the raw value.
277            pub const fn get(self) -> u64 {
278                self.0
279            }
280        }
281
282        impl fmt::Display for $name {
283            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284                write!(f, "{}", self.0)
285            }
286        }
287
288        impl fmt::Debug for $name {
289            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290                write!(f, concat!(stringify!($name), "({})"), self.0)
291            }
292        }
293
294        impl FromStr for $name {
295            type Err = core::num::ParseIntError;
296
297            fn from_str(text: &str) -> Result<Self, Self::Err> {
298                text.parse::<u64>().map(Self)
299            }
300        }
301    };
302}
303
304id64!(
305    /// Numeric table identifier.
306    TableId
307);
308id64!(
309    /// Monotonic schema version of one table or database.
310    SchemaVersion
311);
312id64!(
313    /// Monotonic control-plane metadata version.
314    MetadataVersion
315);
316
317#[cfg(test)]
318mod tests {
319    use super::*;
320    use serde::{Deserialize, Serialize};
321    use std::collections::HashSet;
322
323    // serde_json and bincode are not dependencies of this crate and its
324    // manifest cannot grow them, so the serde contract is exercised through
325    // minimal handwritten harnesses: `StrSerializer` plus serde's built-in
326    // `value::StrDeserializer` stand in for a human-readable format such as
327    // serde_json, and `BinSerializer`/`BinDeserializer` stand in for a
328    // non-human-readable format such as bincode (both treat a newtype struct
329    // transparently, like bincode does).
330
331    #[derive(Debug)]
332    struct HarnessError(String);
333
334    impl fmt::Display for HarnessError {
335        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336            f.write_str(&self.0)
337        }
338    }
339
340    impl std::error::Error for HarnessError {}
341
342    impl serde::ser::Error for HarnessError {
343        fn custom<T: fmt::Display>(msg: T) -> Self {
344            Self(msg.to_string())
345        }
346    }
347
348    impl serde::de::Error for HarnessError {
349        fn custom<T: fmt::Display>(msg: T) -> Self {
350            Self(msg.to_string())
351        }
352    }
353
354    /// Human-readable serializer that only supports strings, mirroring how a
355    /// JSON serializer would carry an ID.
356    struct StrSerializer;
357
358    impl serde::Serializer for StrSerializer {
359        type Ok = String;
360        type Error = HarnessError;
361        type SerializeSeq = serde::ser::Impossible<String, HarnessError>;
362        type SerializeTuple = serde::ser::Impossible<String, HarnessError>;
363        type SerializeTupleStruct = serde::ser::Impossible<String, HarnessError>;
364        type SerializeTupleVariant = serde::ser::Impossible<String, HarnessError>;
365        type SerializeMap = serde::ser::Impossible<String, HarnessError>;
366        type SerializeStruct = serde::ser::Impossible<String, HarnessError>;
367        type SerializeStructVariant = serde::ser::Impossible<String, HarnessError>;
368
369        fn is_human_readable(&self) -> bool {
370            true
371        }
372
373        fn serialize_str(self, v: &str) -> Result<String, HarnessError> {
374            Ok(v.to_owned())
375        }
376
377        fn serialize_bool(self, _v: bool) -> Result<String, HarnessError> {
378            Err(HarnessError("unsupported".into()))
379        }
380        fn serialize_i8(self, _v: i8) -> Result<String, HarnessError> {
381            Err(HarnessError("unsupported".into()))
382        }
383        fn serialize_i16(self, _v: i16) -> Result<String, HarnessError> {
384            Err(HarnessError("unsupported".into()))
385        }
386        fn serialize_i32(self, _v: i32) -> Result<String, HarnessError> {
387            Err(HarnessError("unsupported".into()))
388        }
389        fn serialize_i64(self, _v: i64) -> Result<String, HarnessError> {
390            Err(HarnessError("unsupported".into()))
391        }
392        fn serialize_u8(self, _v: u8) -> Result<String, HarnessError> {
393            Err(HarnessError("unsupported".into()))
394        }
395        fn serialize_u16(self, _v: u16) -> Result<String, HarnessError> {
396            Err(HarnessError("unsupported".into()))
397        }
398        fn serialize_u32(self, _v: u32) -> Result<String, HarnessError> {
399            Err(HarnessError("unsupported".into()))
400        }
401        fn serialize_u64(self, _v: u64) -> Result<String, HarnessError> {
402            Err(HarnessError("unsupported".into()))
403        }
404        fn serialize_f32(self, _v: f32) -> Result<String, HarnessError> {
405            Err(HarnessError("unsupported".into()))
406        }
407        fn serialize_f64(self, _v: f64) -> Result<String, HarnessError> {
408            Err(HarnessError("unsupported".into()))
409        }
410        fn serialize_char(self, _v: char) -> Result<String, HarnessError> {
411            Err(HarnessError("unsupported".into()))
412        }
413        fn serialize_bytes(self, _v: &[u8]) -> Result<String, HarnessError> {
414            Err(HarnessError("unsupported".into()))
415        }
416        fn serialize_none(self) -> Result<String, HarnessError> {
417            Err(HarnessError("unsupported".into()))
418        }
419        fn serialize_some<T: ?Sized + serde::Serialize>(
420            self,
421            _value: &T,
422        ) -> Result<String, HarnessError> {
423            Err(HarnessError("unsupported".into()))
424        }
425        fn serialize_unit(self) -> Result<String, HarnessError> {
426            Err(HarnessError("unsupported".into()))
427        }
428        fn serialize_unit_struct(self, _name: &'static str) -> Result<String, HarnessError> {
429            Err(HarnessError("unsupported".into()))
430        }
431        fn serialize_unit_variant(
432            self,
433            _name: &'static str,
434            _variant_index: u32,
435            _variant: &'static str,
436        ) -> Result<String, HarnessError> {
437            Err(HarnessError("unsupported".into()))
438        }
439        fn serialize_newtype_struct<T: ?Sized + serde::Serialize>(
440            self,
441            _name: &'static str,
442            _value: &T,
443        ) -> Result<String, HarnessError> {
444            Err(HarnessError("unsupported".into()))
445        }
446        fn serialize_newtype_variant<T: ?Sized + serde::Serialize>(
447            self,
448            _name: &'static str,
449            _variant_index: u32,
450            _variant: &'static str,
451            _value: &T,
452        ) -> Result<String, HarnessError> {
453            Err(HarnessError("unsupported".into()))
454        }
455        fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, HarnessError> {
456            Err(HarnessError("unsupported".into()))
457        }
458        fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, HarnessError> {
459            Err(HarnessError("unsupported".into()))
460        }
461        fn serialize_tuple_struct(
462            self,
463            _name: &'static str,
464            _len: usize,
465        ) -> Result<Self::SerializeTupleStruct, HarnessError> {
466            Err(HarnessError("unsupported".into()))
467        }
468        fn serialize_tuple_variant(
469            self,
470            _name: &'static str,
471            _variant_index: u32,
472            _variant: &'static str,
473            _len: usize,
474        ) -> Result<Self::SerializeTupleVariant, HarnessError> {
475            Err(HarnessError("unsupported".into()))
476        }
477        fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, HarnessError> {
478            Err(HarnessError("unsupported".into()))
479        }
480        fn serialize_struct(
481            self,
482            _name: &'static str,
483            _len: usize,
484        ) -> Result<Self::SerializeStruct, HarnessError> {
485            Err(HarnessError("unsupported".into()))
486        }
487        fn serialize_struct_variant(
488            self,
489            _name: &'static str,
490            _variant_index: u32,
491            _variant: &'static str,
492            _len: usize,
493        ) -> Result<Self::SerializeStructVariant, HarnessError> {
494            Err(HarnessError("unsupported".into()))
495        }
496    }
497
498    /// Non-human-readable serializer that flattens an ID to its raw bytes,
499    /// treating newtype structs transparently like bincode does.
500    struct BinSerializer;
501
502    struct ByteCollector {
503        buf: Vec<u8>,
504    }
505
506    impl ByteCollector {
507        fn push<T: ?Sized + serde::Serialize>(&mut self, value: &T) -> Result<(), HarnessError> {
508            let mut bytes = value.serialize(BinSerializer)?;
509            self.buf.append(&mut bytes);
510            Ok(())
511        }
512    }
513
514    impl serde::ser::SerializeSeq for ByteCollector {
515        type Ok = Vec<u8>;
516        type Error = HarnessError;
517
518        fn serialize_element<T: ?Sized + serde::Serialize>(
519            &mut self,
520            value: &T,
521        ) -> Result<(), HarnessError> {
522            self.push(value)
523        }
524
525        fn end(self) -> Result<Vec<u8>, HarnessError> {
526            Ok(self.buf)
527        }
528    }
529
530    impl serde::ser::SerializeTuple for ByteCollector {
531        type Ok = Vec<u8>;
532        type Error = HarnessError;
533
534        fn serialize_element<T: ?Sized + serde::Serialize>(
535            &mut self,
536            value: &T,
537        ) -> Result<(), HarnessError> {
538            self.push(value)
539        }
540
541        fn end(self) -> Result<Vec<u8>, HarnessError> {
542            Ok(self.buf)
543        }
544    }
545
546    impl serde::Serializer for BinSerializer {
547        type Ok = Vec<u8>;
548        type Error = HarnessError;
549        type SerializeSeq = ByteCollector;
550        type SerializeTuple = ByteCollector;
551        type SerializeTupleStruct = serde::ser::Impossible<Vec<u8>, HarnessError>;
552        type SerializeTupleVariant = serde::ser::Impossible<Vec<u8>, HarnessError>;
553        type SerializeMap = serde::ser::Impossible<Vec<u8>, HarnessError>;
554        type SerializeStruct = serde::ser::Impossible<Vec<u8>, HarnessError>;
555        type SerializeStructVariant = serde::ser::Impossible<Vec<u8>, HarnessError>;
556
557        fn is_human_readable(&self) -> bool {
558            false
559        }
560
561        fn serialize_u8(self, v: u8) -> Result<Vec<u8>, HarnessError> {
562            Ok(vec![v])
563        }
564
565        fn serialize_bytes(self, v: &[u8]) -> Result<Vec<u8>, HarnessError> {
566            Ok(v.to_vec())
567        }
568
569        fn serialize_newtype_struct<T: ?Sized + serde::Serialize>(
570            self,
571            _name: &'static str,
572            value: &T,
573        ) -> Result<Vec<u8>, HarnessError> {
574            value.serialize(self)
575        }
576
577        fn serialize_seq(self, len: Option<usize>) -> Result<ByteCollector, HarnessError> {
578            Ok(ByteCollector {
579                buf: Vec::with_capacity(len.unwrap_or(0)),
580            })
581        }
582
583        fn serialize_tuple(self, len: usize) -> Result<ByteCollector, HarnessError> {
584            Ok(ByteCollector {
585                buf: Vec::with_capacity(len),
586            })
587        }
588
589        fn serialize_bool(self, _v: bool) -> Result<Vec<u8>, HarnessError> {
590            Err(HarnessError("unsupported".into()))
591        }
592        fn serialize_i8(self, _v: i8) -> Result<Vec<u8>, HarnessError> {
593            Err(HarnessError("unsupported".into()))
594        }
595        fn serialize_i16(self, _v: i16) -> Result<Vec<u8>, HarnessError> {
596            Err(HarnessError("unsupported".into()))
597        }
598        fn serialize_i32(self, _v: i32) -> Result<Vec<u8>, HarnessError> {
599            Err(HarnessError("unsupported".into()))
600        }
601        fn serialize_i64(self, _v: i64) -> Result<Vec<u8>, HarnessError> {
602            Err(HarnessError("unsupported".into()))
603        }
604        fn serialize_u16(self, _v: u16) -> Result<Vec<u8>, HarnessError> {
605            Err(HarnessError("unsupported".into()))
606        }
607        fn serialize_u32(self, _v: u32) -> Result<Vec<u8>, HarnessError> {
608            Err(HarnessError("unsupported".into()))
609        }
610        fn serialize_u64(self, _v: u64) -> Result<Vec<u8>, HarnessError> {
611            Err(HarnessError("unsupported".into()))
612        }
613        fn serialize_f32(self, _v: f32) -> Result<Vec<u8>, HarnessError> {
614            Err(HarnessError("unsupported".into()))
615        }
616        fn serialize_f64(self, _v: f64) -> Result<Vec<u8>, HarnessError> {
617            Err(HarnessError("unsupported".into()))
618        }
619        fn serialize_char(self, _v: char) -> Result<Vec<u8>, HarnessError> {
620            Err(HarnessError("unsupported".into()))
621        }
622        fn serialize_str(self, _v: &str) -> Result<Vec<u8>, HarnessError> {
623            Err(HarnessError("unsupported".into()))
624        }
625        fn serialize_none(self) -> Result<Vec<u8>, HarnessError> {
626            Err(HarnessError("unsupported".into()))
627        }
628        fn serialize_some<T: ?Sized + serde::Serialize>(
629            self,
630            _value: &T,
631        ) -> Result<Vec<u8>, HarnessError> {
632            Err(HarnessError("unsupported".into()))
633        }
634        fn serialize_unit(self) -> Result<Vec<u8>, HarnessError> {
635            Err(HarnessError("unsupported".into()))
636        }
637        fn serialize_unit_struct(self, _name: &'static str) -> Result<Vec<u8>, HarnessError> {
638            Err(HarnessError("unsupported".into()))
639        }
640        fn serialize_unit_variant(
641            self,
642            _name: &'static str,
643            _variant_index: u32,
644            _variant: &'static str,
645        ) -> Result<Vec<u8>, HarnessError> {
646            Err(HarnessError("unsupported".into()))
647        }
648        fn serialize_newtype_variant<T: ?Sized + serde::Serialize>(
649            self,
650            _name: &'static str,
651            _variant_index: u32,
652            _variant: &'static str,
653            _value: &T,
654        ) -> Result<Vec<u8>, HarnessError> {
655            Err(HarnessError("unsupported".into()))
656        }
657        fn serialize_tuple_struct(
658            self,
659            _name: &'static str,
660            _len: usize,
661        ) -> Result<Self::SerializeTupleStruct, HarnessError> {
662            Err(HarnessError("unsupported".into()))
663        }
664        fn serialize_tuple_variant(
665            self,
666            _name: &'static str,
667            _variant_index: u32,
668            _variant: &'static str,
669            _len: usize,
670        ) -> Result<Self::SerializeTupleVariant, HarnessError> {
671            Err(HarnessError("unsupported".into()))
672        }
673        fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, HarnessError> {
674            Err(HarnessError("unsupported".into()))
675        }
676        fn serialize_struct(
677            self,
678            _name: &'static str,
679            _len: usize,
680        ) -> Result<Self::SerializeStruct, HarnessError> {
681            Err(HarnessError("unsupported".into()))
682        }
683        fn serialize_struct_variant(
684            self,
685            _name: &'static str,
686            _variant_index: u32,
687            _variant: &'static str,
688            _len: usize,
689        ) -> Result<Self::SerializeStructVariant, HarnessError> {
690            Err(HarnessError("unsupported".into()))
691        }
692    }
693
694    /// Non-human-readable deserializer over a byte cursor, treating newtype
695    /// structs transparently like bincode does. Callers request exactly as
696    /// many elements as they need, so no length bookkeeping is required.
697    struct BinDeserializer<'de> {
698        input: &'de [u8],
699    }
700
701    impl<'de> BinDeserializer<'de> {
702        fn new(input: &'de [u8]) -> Self {
703            Self { input }
704        }
705    }
706
707    impl<'de> serde::Deserializer<'de> for &mut BinDeserializer<'de> {
708        type Error = HarnessError;
709
710        fn is_human_readable(&self) -> bool {
711            false
712        }
713
714        fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value, HarnessError>
715        where
716            V: serde::de::Visitor<'de>,
717        {
718            Err(HarnessError("unsupported".into()))
719        }
720
721        fn deserialize_newtype_struct<V>(
722            self,
723            _name: &'static str,
724            visitor: V,
725        ) -> Result<V::Value, HarnessError>
726        where
727            V: serde::de::Visitor<'de>,
728        {
729            visitor.visit_newtype_struct(self)
730        }
731
732        fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, HarnessError>
733        where
734            V: serde::de::Visitor<'de>,
735        {
736            visitor.visit_seq(ByteSeq { de: self })
737        }
738
739        fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, HarnessError>
740        where
741            V: serde::de::Visitor<'de>,
742        {
743            visitor.visit_seq(ByteSeq { de: self })
744        }
745
746        fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, HarnessError>
747        where
748            V: serde::de::Visitor<'de>,
749        {
750            let (&byte, rest) = self
751                .input
752                .split_first()
753                .ok_or_else(|| HarnessError("unexpected end of input".into()))?;
754            self.input = rest;
755            visitor.visit_u8(byte)
756        }
757
758        serde::forward_to_deserialize_any! {
759            bool i8 i16 i32 i64 u16 u32 u64 f32 f64 char str string
760            bytes byte_buf option unit unit_struct
761            tuple_struct map struct enum identifier ignored_any
762        }
763    }
764
765    struct ByteSeq<'a, 'de> {
766        de: &'a mut BinDeserializer<'de>,
767    }
768
769    impl<'de> serde::de::SeqAccess<'de> for ByteSeq<'_, 'de> {
770        type Error = HarnessError;
771
772        fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, HarnessError>
773        where
774            T: serde::de::DeserializeSeed<'de>,
775        {
776            seed.deserialize(&mut *self.de).map(Some)
777        }
778    }
779
780    fn check_text_forms<T>(id: T)
781    where
782        T: Copy + PartialEq + fmt::Debug + fmt::Display + FromStr<Err = IdParseError>,
783    {
784        let text = id.to_string();
785        assert_eq!(text.len(), 32, "canonical form is 32 chars");
786        assert!(
787            text.chars()
788                .all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase()),
789            "canonical form is strict lowercase hex: {text}"
790        );
791        assert_eq!(text.parse::<T>().unwrap(), id, "hex round-trip");
792
793        let uuid = format!(
794            "{}-{}-{}-{}-{}",
795            &text[0..8],
796            &text[8..12],
797            &text[12..16],
798            &text[16..20],
799            &text[20..32]
800        );
801        assert_eq!(
802            uuid.parse::<T>().unwrap(),
803            id,
804            "hyphenated UUID form parses"
805        );
806
807        let upper = text.to_ascii_uppercase();
808        assert_eq!(upper.parse::<T>().unwrap(), id, "uppercase hex parses");
809
810        let upper_uuid = uuid.to_ascii_uppercase();
811        assert_eq!(
812            upper_uuid.parse::<T>().unwrap(),
813            id,
814            "uppercase UUID form parses"
815        );
816    }
817
818    #[test]
819    fn text_forms_round_trip_all_types() {
820        check_text_forms(ClusterId::new_random());
821        check_text_forms(NodeId::new_random());
822        check_text_forms(DatabaseId::new_random());
823        check_text_forms(TabletId::new_random());
824        check_text_forms(RaftGroupId::new_random());
825        check_text_forms(TransactionId::new_random());
826        check_text_forms(QueryId::new_random());
827    }
828
829    #[test]
830    fn text_form_exact_known_value() {
831        let bytes = [
832            0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76,
833            0x54, 0x32,
834        ];
835        let id = ClusterId::from_bytes(bytes);
836        let expected = "000123456789abcdeffedcba98765432";
837        assert_eq!(id.to_hex(), expected);
838        assert_eq!(id.to_string(), expected);
839        assert_eq!(format!("{id:?}"), format!("ClusterId({expected})"));
840        assert_eq!(expected.parse::<ClusterId>().unwrap(), id);
841        assert_eq!(ClusterId::from_bytes(bytes).as_bytes(), &bytes);
842    }
843
844    #[test]
845    fn parse_rejects_bad_input() {
846        assert_eq!(
847            "".parse::<ClusterId>(),
848            Err(IdParseError::InvalidLength(0)),
849            "empty input"
850        );
851        assert_eq!(
852            "abcd".parse::<ClusterId>(),
853            Err(IdParseError::InvalidLength(4)),
854            "too short"
855        );
856        assert_eq!(
857            "a".repeat(33).parse::<ClusterId>(),
858            Err(IdParseError::InvalidLength(33)),
859            "too long"
860        );
861        assert_eq!(
862            "--------------------------------".parse::<ClusterId>(),
863            Err(IdParseError::InvalidLength(0)),
864            "hyphens only"
865        );
866        assert_eq!(
867            "g".repeat(32).parse::<ClusterId>(),
868            Err(IdParseError::InvalidCharacter('g')),
869            "non-hex character"
870        );
871        assert_eq!(
872            format!("{}é", "a".repeat(31)).parse::<ClusterId>(),
873            Err(IdParseError::InvalidCharacter('é')),
874            "non-ASCII character"
875        );
876    }
877
878    fn check_human_readable_serde<T>(id: T)
879    where
880        T: Copy + PartialEq + fmt::Debug + fmt::Display + FromStr<Err = IdParseError>,
881        T: Serialize + for<'de> Deserialize<'de>,
882    {
883        let json_like = id.serialize(StrSerializer).unwrap();
884        assert_eq!(
885            json_like,
886            id.to_string(),
887            "human-readable form is the hex string"
888        );
889
890        let back: T = T::deserialize(serde::de::value::StrDeserializer::<HarnessError>::new(
891            &json_like,
892        ))
893        .unwrap();
894        assert_eq!(back, id, "human-readable string round-trip");
895
896        let upper: T = T::deserialize(serde::de::value::StrDeserializer::<HarnessError>::new(
897            &json_like.to_ascii_uppercase(),
898        ))
899        .unwrap();
900        assert_eq!(upper, id, "human-readable form accepts uppercase");
901
902        let invalid = T::deserialize(serde::de::value::StrDeserializer::<HarnessError>::new(
903            "not-an-id",
904        ));
905        assert!(
906            invalid.is_err(),
907            "human-readable form rejects invalid strings"
908        );
909    }
910
911    #[test]
912    fn human_readable_serde_round_trip_all_types() {
913        check_human_readable_serde(ClusterId::new_random());
914        check_human_readable_serde(NodeId::new_random());
915        check_human_readable_serde(DatabaseId::new_random());
916        check_human_readable_serde(TabletId::new_random());
917        check_human_readable_serde(RaftGroupId::new_random());
918        check_human_readable_serde(TransactionId::new_random());
919        check_human_readable_serde(QueryId::new_random());
920    }
921
922    fn check_binary_serde<T>(id: T, raw: &[u8; 16])
923    where
924        T: Copy + PartialEq + fmt::Debug + Serialize + for<'de> Deserialize<'de>,
925    {
926        let bytes = id.serialize(BinSerializer).unwrap();
927        assert_eq!(bytes.len(), 16, "binary form stays 16 bytes");
928        assert_eq!(
929            bytes.as_slice(),
930            raw.as_slice(),
931            "binary form is the raw bytes"
932        );
933
934        let mut de = BinDeserializer::new(&bytes);
935        let back = T::deserialize(&mut de).unwrap();
936        assert_eq!(back, id, "binary round-trip");
937        assert!(de.input.is_empty(), "binary form consumed exactly");
938    }
939
940    #[test]
941    fn binary_serde_stays_sixteen_bytes_all_types() {
942        macro_rules! check {
943            ($($name:ident),*) => {$(
944                let id = $name::new_random();
945                check_binary_serde(id, id.as_bytes());
946            )*};
947        }
948        check!(
949            ClusterId,
950            NodeId,
951            DatabaseId,
952            TabletId,
953            RaftGroupId,
954            TransactionId,
955            QueryId
956        );
957    }
958
959    #[test]
960    fn zero_constant_is_all_zero() {
961        assert_eq!(ClusterId::ZERO.as_bytes(), &[0u8; 16]);
962        assert_eq!(ClusterId::ZERO.to_hex(), "0".repeat(32));
963        assert_eq!(
964            "0".repeat(32).parse::<ClusterId>().unwrap(),
965            ClusterId::ZERO
966        );
967        assert_eq!(TableId::ZERO.get(), 0);
968        assert_eq!(SchemaVersion::ZERO.get(), 0);
969        assert_eq!(MetadataVersion::ZERO.get(), 0);
970    }
971
972    #[test]
973    fn ordering_follows_byte_order() {
974        let low = ClusterId::from_bytes([0x00; 16]);
975        let mut high_bytes = [0x00; 16];
976        high_bytes[15] = 0x01;
977        let high = ClusterId::from_bytes(high_bytes);
978        assert!(low < high);
979        assert!(ClusterId::ZERO <= low);
980
981        let mut ids = vec![high, ClusterId::ZERO, low];
982        ids.sort();
983        assert_eq!(ids, vec![ClusterId::ZERO, low, high]);
984
985        assert!(TableId::new(1) < TableId::new(2));
986    }
987
988    #[test]
989    fn new_random_is_distinct_and_nonzero() {
990        let mut seen = HashSet::with_capacity(1000);
991        for _ in 0..1000 {
992            let id = ClusterId::new_random();
993            assert_ne!(
994                id,
995                ClusterId::ZERO,
996                "random ID must not be the reserved zero value"
997            );
998            assert!(seen.insert(id), "random IDs must not repeat");
999        }
1000    }
1001
1002    #[test]
1003    fn id64_round_trips() {
1004        let id = TableId::new(42);
1005        assert_eq!(id.get(), 42);
1006        assert_eq!(id.to_string(), "42");
1007        assert_eq!("42".parse::<TableId>().unwrap(), id);
1008        assert!("not-a-number".parse::<TableId>().is_err());
1009        assert_eq!(format!("{id:?}"), "TableId(42)");
1010    }
1011}