Skip to main content

peat_lite/
node_id.rs

1// Copyright (c) 2025-2026 Defense Unicorns, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Node identifier type.
5//!
6//! A 32-bit identifier for nodes in the mesh. This is intentionally smaller
7//! than a full UUID to fit memory constraints on embedded devices.
8
9/// A 32-bit node identifier.
10///
11/// Derived from BLE MAC address or assigned during provisioning.
12/// Collision probability is acceptable for tactical mesh sizes (<1000 nodes).
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
14#[repr(transparent)]
15pub struct NodeId(u32);
16
17impl NodeId {
18    /// Create a new NodeId from a 32-bit value.
19    #[inline]
20    pub const fn new(id: u32) -> Self {
21        Self(id)
22    }
23
24    /// Create a NodeId from bytes (little-endian).
25    #[inline]
26    pub const fn from_le_bytes(bytes: [u8; 4]) -> Self {
27        Self(u32::from_le_bytes(bytes))
28    }
29
30    /// Create a NodeId from bytes (big-endian).
31    #[inline]
32    pub const fn from_be_bytes(bytes: [u8; 4]) -> Self {
33        Self(u32::from_be_bytes(bytes))
34    }
35
36    /// Get the raw u32 value.
37    #[inline]
38    pub const fn as_u32(self) -> u32 {
39        self.0
40    }
41
42    /// Convert to little-endian bytes.
43    #[inline]
44    pub const fn to_le_bytes(self) -> [u8; 4] {
45        self.0.to_le_bytes()
46    }
47
48    /// Convert to big-endian bytes.
49    #[inline]
50    pub const fn to_be_bytes(self) -> [u8; 4] {
51        self.0.to_be_bytes()
52    }
53
54    /// Check if this is the null/invalid node ID.
55    #[inline]
56    pub const fn is_null(self) -> bool {
57        self.0 == 0
58    }
59
60    /// The null/invalid node ID.
61    pub const NULL: Self = Self(0);
62}
63
64impl From<u32> for NodeId {
65    #[inline]
66    fn from(id: u32) -> Self {
67        Self(id)
68    }
69}
70
71impl From<NodeId> for u32 {
72    #[inline]
73    fn from(id: NodeId) -> Self {
74        id.0
75    }
76}
77
78#[cfg(feature = "std")]
79impl core::fmt::Display for NodeId {
80    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81        write!(f, "{:08X}", self.0)
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_node_id_roundtrip() {
91        let id = NodeId::new(0xDEADBEEF);
92        let bytes = id.to_le_bytes();
93        let recovered = NodeId::from_le_bytes(bytes);
94        assert_eq!(id, recovered);
95    }
96
97    #[test]
98    fn test_node_id_null() {
99        assert!(NodeId::NULL.is_null());
100        assert!(NodeId::new(0).is_null());
101        assert!(!NodeId::new(1).is_null());
102    }
103}