Skip to main content

bibeam_core/
ids.rs

1#![forbid(unsafe_code)]
2//! ULID newtypes for the `BiBeam` identity space.
3//!
4//! Four distinct wrappers — [`PeerId`], [`NodeId`], [`CohortId`], [`ChainId`]
5//! — sit over [`ulid::Ulid`] so the type system can distinguish a peer from a
6//! node from a cohort from a multi-hop forwarder chain, even though they all
7//! share the same wire encoding (a 128-bit Crockford-Base32 ULID).
8
9use core::{fmt, str::FromStr};
10
11use serde::{Deserialize, Serialize};
12use ulid::Ulid;
13
14macro_rules! ulid_newtype {
15    (
16        $(#[$doc:meta])*
17        $name:ident
18    ) => {
19        $(#[$doc])*
20        #[derive(
21            Clone,
22            Copy,
23            Debug,
24            PartialEq,
25            Eq,
26            Hash,
27            PartialOrd,
28            Ord,
29            Serialize,
30            Deserialize,
31        )]
32        #[serde(transparent)]
33        pub struct $name(pub Ulid);
34
35        impl $name {
36            /// Generate a fresh identifier using the system clock plus
37            /// [`ulid::Ulid::new`]'s default RNG.
38            #[must_use]
39            #[allow(
40                clippy::new_without_default,
41                reason = "Default::default() returns Ulid::nil() — a zero ULID — \
42                          which is observably different from a freshly generated ULID; \
43                          we deliberately do not derive Default to avoid that surprise."
44            )]
45            pub fn new() -> Self {
46                Self(Ulid::new())
47            }
48
49            /// Borrow the underlying [`Ulid`].
50            #[must_use]
51            pub const fn as_ulid(&self) -> &Ulid {
52                &self.0
53            }
54
55            /// Consume the newtype and return the underlying [`Ulid`].
56            #[must_use]
57            pub const fn into_ulid(self) -> Ulid {
58                self.0
59            }
60        }
61
62        impl fmt::Display for $name {
63            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64                fmt::Display::fmt(&self.0, f)
65            }
66        }
67
68        impl FromStr for $name {
69            type Err = ulid::DecodeError;
70
71            fn from_str(s: &str) -> Result<Self, Self::Err> {
72                Ulid::from_string(s).map(Self)
73            }
74        }
75    };
76}
77
78ulid_newtype! {
79    /// Identifier for a single remote peer in the `BiBeam` mesh.
80    PeerId
81}
82
83ulid_newtype! {
84    /// Identifier for a local node (this process's view of itself).
85    NodeId
86}
87
88ulid_newtype! {
89    /// Identifier for a cohort — a logical grouping of peers that share a
90    /// trust or routing scope.
91    CohortId
92}
93
94ulid_newtype! {
95    /// Identifier for a multi-hop forwarder chain.
96    ///
97    /// Coordinator-issued opaque handle that every forwarder along a
98    /// `MatchResponse::MultiHopAssignment` chain uses to look up the
99    /// row in its lease table that authorises a given packet flow.
100    /// Lives independently of [`CohortId`] because one cohort can have
101    /// many concurrent multi-hop assignments, and one chain may serve
102    /// peers from different cohorts at different times.
103    ChainId
104}