1use serde::{Deserialize, Serialize};
8
9use crate::error::{Error, Result};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
17pub struct Id16(pub [u8; 16]);
18
19impl Id16 {
20 pub fn random() -> Self {
22 use rand::RngCore;
23 let mut bytes = [0u8; 16];
24 rand::thread_rng().fill_bytes(&mut bytes);
25 Self(bytes)
26 }
27
28 pub fn from_bytes(bytes: [u8; 16]) -> Self {
30 Self(bytes)
31 }
32
33 pub fn as_bytes(&self) -> &[u8; 16] {
35 &self.0
36 }
37
38 pub fn to_hex(&self) -> String {
40 hex::encode(self.0)
41 }
42
43 pub fn from_hex(s: &str) -> Result<Self> {
45 let bytes = hex::decode(s).map_err(|_| Error::invalid_input("invalid hex"))?;
46 if bytes.len() != 16 {
47 return Err(Error::invalid_input("ID must be 16 bytes"));
48 }
49 let mut arr = [0u8; 16];
50 arr.copy_from_slice(&bytes);
51 Ok(Self(arr))
52 }
53}
54
55impl std::fmt::Display for Id16 {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 write!(f, "{}", self.to_hex())
58 }
59}
60
61#[macro_export]
77macro_rules! define_id {
78 ($name:ident, $desc:expr) => {
79 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
80 #[allow(dead_code)]
81 pub struct $name(pub [u8; 16]);
82
83 #[allow(dead_code, clippy::wrong_self_convention)]
84 impl $name {
85 pub fn generate() -> Self {
87 let inner = $crate::agent::id::Id16::random();
88 Self(inner.0)
89 }
90
91 pub fn from_bytes(bytes: [u8; 16]) -> Self {
93 Self(bytes)
94 }
95
96 pub fn as_bytes(&self) -> &[u8; 16] {
98 &self.0
99 }
100
101 pub fn to_hex(&self) -> String {
103 hex::encode(self.0)
104 }
105
106 #[doc = concat!("Parse a ", $desc, " from a hex string.")]
107 pub fn from_hex(s: &str) -> $crate::error::Result<Self> {
108 let inner = $crate::agent::id::Id16::from_hex(s)?;
109 Ok(Self(inner.0))
110 }
111 }
112
113 impl std::fmt::Display for $name {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 write!(f, "{}", self.to_hex())
116 }
117 }
118 };
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn random_id_uniqueness() {
127 let id1 = Id16::random();
128 let id2 = Id16::random();
129 assert_ne!(id1, id2);
130 }
131
132 #[test]
133 fn random_id_hex_roundtrip() {
134 let id = Id16::random();
135 let hex = id.to_hex();
136 let restored = Id16::from_hex(&hex).unwrap();
137 assert_eq!(id, restored);
138 }
139
140 #[test]
141 fn random_id_display() {
142 let id = Id16::random();
143 let display = format!("{}", id);
144 assert_eq!(display.len(), 32); }
146
147 #[test]
148 fn define_id_macro_works() {
149 define_id!(TestId, "test identifier");
150
151 let id1 = TestId::generate();
152 let id2 = TestId::generate();
153 assert_ne!(id1, id2);
154
155 let hex = id1.to_hex();
156 let restored = TestId::from_hex(&hex).unwrap();
157 assert_eq!(id1, restored);
158 assert_eq!(format!("{}", id1).len(), 32);
159 }
160}