use serde::{Deserialize, Serialize};
use crate::error::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Id16(pub [u8; 16]);
impl Id16 {
pub fn random() -> Self {
use rand::RngCore;
let mut bytes = [0u8; 16];
rand::thread_rng().fill_bytes(&mut bytes);
Self(bytes)
}
pub fn from_bytes(bytes: [u8; 16]) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8; 16] {
&self.0
}
pub fn to_hex(&self) -> String {
hex::encode(self.0)
}
pub fn from_hex(s: &str) -> Result<Self> {
let bytes = hex::decode(s).map_err(|_| Error::invalid_input("invalid hex"))?;
if bytes.len() != 16 {
return Err(Error::invalid_input("ID must be 16 bytes"));
}
let mut arr = [0u8; 16];
arr.copy_from_slice(&bytes);
Ok(Self(arr))
}
}
impl std::fmt::Display for Id16 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_hex())
}
}
#[macro_export]
macro_rules! define_id {
($name:ident, $desc:expr) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[allow(dead_code)]
pub struct $name(pub [u8; 16]);
#[allow(dead_code, clippy::wrong_self_convention)]
impl $name {
pub fn generate() -> Self {
let inner = $crate::agent::id::Id16::random();
Self(inner.0)
}
pub fn from_bytes(bytes: [u8; 16]) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8; 16] {
&self.0
}
pub fn to_hex(&self) -> String {
hex::encode(self.0)
}
#[doc = concat!("Parse a ", $desc, " from a hex string.")]
pub fn from_hex(s: &str) -> $crate::error::Result<Self> {
let inner = $crate::agent::id::Id16::from_hex(s)?;
Ok(Self(inner.0))
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_hex())
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn random_id_uniqueness() {
let id1 = Id16::random();
let id2 = Id16::random();
assert_ne!(id1, id2);
}
#[test]
fn random_id_hex_roundtrip() {
let id = Id16::random();
let hex = id.to_hex();
let restored = Id16::from_hex(&hex).unwrap();
assert_eq!(id, restored);
}
#[test]
fn random_id_display() {
let id = Id16::random();
let display = format!("{}", id);
assert_eq!(display.len(), 32); }
#[test]
fn define_id_macro_works() {
define_id!(TestId, "test identifier");
let id1 = TestId::generate();
let id2 = TestId::generate();
assert_ne!(id1, id2);
let hex = id1.to_hex();
let restored = TestId::from_hex(&hex).unwrap();
assert_eq!(id1, restored);
assert_eq!(format!("{}", id1).len(), 32);
}
}