Skip to main content

bb_ir/
ids.rs

1//! Wire-format and compiler-bound identifier types.
2//!
3//! IDs tied to the IR or wire envelope live here ([`PeerId`],
4//! [`RequestId`], [`OpsetId`], [`ComponentTag`]). Engine-internal
5//! dispatch IDs (`NodeSiteId`, `OpRef`, `ExecId`, `CommandId`,
6//! `ComponentRef`) live in `bb_runtime::ids`.
7
8use std::fmt;
9
10// --- Macro helpers ----------------------------------------------
11
12macro_rules! u64_id {
13    ($(#[$attr:meta])* $name:ident) => {
14        $(#[$attr])*
15        #[derive(
16            Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord,
17            serde::Serialize, serde::Deserialize,
18        )]
19        #[repr(transparent)]
20        pub struct $name(u64);
21
22        impl $name {
23            /// Construct from an explicit value.
24            pub const fn new(inner: u64) -> Self { Self(inner) }
25
26            /// Inner value accessor.
27            pub const fn as_u64(self) -> u64 { self.0 }
28        }
29
30        impl fmt::Display for $name {
31            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32                write!(f, "{}({})", stringify!($name), self.0)
33            }
34        }
35
36        impl From<u64> for $name {
37            fn from(inner: u64) -> Self { Self(inner) }
38        }
39    };
40}
41
42// --- Wire-format / compiler-bound IDs ---------------------------
43
44u64_id! {
45    /// Per-request correlation token. Issued by `SendReqBatched`
46    /// senders; echoed by receivers in `SendResp`.
47    RequestId
48}
49
50// --- Symbolic IDs -----------------------------------------------
51
52/// Typed bus-subscription tag - a static `&str` label naming the
53/// kind of event a component receives.
54#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
55pub struct ComponentTag(pub &'static str);
56
57impl ComponentTag {
58    /// Construct from an explicit static label.
59    pub const fn new(tag: &'static str) -> Self {
60        Self(tag)
61    }
62
63    /// Inner label accessor.
64    pub const fn as_str(self) -> &'static str {
65        self.0
66    }
67}
68
69impl fmt::Display for ComponentTag {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        write!(f, "ComponentTag({:?})", self.0)
72    }
73}
74
75/// Opset identifier. `(domain, version)` pair routing wire-level
76/// dispatch. See `docs/IR_AND_DSL.md` §5 for the canonical opset
77/// catalog.
78#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
79pub struct OpsetId {
80    /// Reverse-DNS-ish domain (`ai.onnx`, `bb.wire`, `bb.gossip`,
81    /// `user.kademlia`, ...).
82    pub domain: &'static str,
83
84    /// Major version. Minor/patch live in the component's own
85    /// versioning surface; this field gates wire-level
86    /// compatibility.
87    pub version: i64,
88}
89
90impl OpsetId {
91    /// Construct from explicit values.
92    pub const fn new(domain: &'static str, version: i64) -> Self {
93        Self { domain, version }
94    }
95}
96
97impl fmt::Display for OpsetId {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        write!(f, "{} v{}", self.domain, self.version)
100    }
101}
102
103// --- PeerId - libp2p-compatible multihash -----------------------
104
105/// Peer identity. Wraps a fixed-capacity `Multihash<64>` so
106/// overlays pick their own digest algorithm. Same wire shape as
107/// `libp2p_identity::PeerId`; bytes round-trip without translation.
108#[derive(
109    Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
110)]
111#[repr(transparent)]
112pub struct PeerId(multihash::Multihash<64>);
113
114impl PeerId {
115    /// Multihash code for `identity` (raw bytes, no hashing).
116    pub const IDENTITY: u64 = 0x00;
117
118    /// Multihash code for `sha2-256`.
119    pub const SHA2_256: u64 = 0x12;
120
121    /// Construct from a multihash directly.
122    pub const fn from_multihash(mh: multihash::Multihash<64>) -> Self {
123        Self(mh)
124    }
125
126    /// Parse the canonical multihash byte form
127    /// (`varint(code) ++ varint(size) ++ digest`). Wire-format
128    /// compatible with libp2p.
129    pub fn from_bytes(bytes: &[u8]) -> Result<Self, multihash::Error> {
130        multihash::Multihash::from_bytes(bytes).map(Self)
131    }
132
133    /// Canonical multihash byte form, matches libp2p's
134    /// `PeerId::to_bytes`.
135    pub fn to_bytes(&self) -> Vec<u8> {
136        self.0.to_bytes()
137    }
138
139    /// Multihash algorithm code (e.g. `0x00` identity, `0x12`
140    /// sha2-256).
141    pub fn code(&self) -> u64 {
142        self.0.code()
143    }
144
145    /// Raw digest bytes (after the multihash code + size prefix).
146    pub fn digest(&self) -> &[u8] {
147        self.0.digest()
148    }
149
150    /// Borrow the inner multihash.
151    pub fn as_multihash(&self) -> &multihash::Multihash<64> {
152        &self.0
153    }
154
155    /// Recover the inner u64 of a `PeerId::from(u64)`. Returns
156    /// `None` for non-identity-coded or non-8-byte digests.
157    pub fn as_identity_u64(&self) -> Option<u64> {
158        if self.0.code() == Self::IDENTITY {
159            let d = self.0.digest();
160            if d.len() == 8 {
161                return Some(u64::from_be_bytes(d.try_into().expect("checked len == 8")));
162            }
163        }
164        None
165    }
166}
167
168impl From<u64> for PeerId {
169    /// Test convenience: wraps the u64 as an 8-byte identity-coded
170    /// multihash. **Not for production identity** — use
171    /// `PeerId::from_multihash` / `from_bytes` for keyed identities.
172    fn from(value: u64) -> Self {
173        let bytes = value.to_be_bytes();
174        let mh = multihash::Multihash::<64>::wrap(Self::IDENTITY, &bytes)
175            .expect("identity hash of 8 bytes always fits in 64");
176        Self(mh)
177    }
178}
179
180impl fmt::Display for PeerId {
181    /// Base58btc-encoded multihash; matches libp2p's `PeerId::to_base58`.
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        f.write_str(&bs58::encode(self.to_bytes()).into_string())
184    }
185}
186