use core::{
convert::{TryFrom, TryInto},
fmt::{self},
hash::Hash,
num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8},
str::FromStr,
};
use rand::Rng;
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(transparent)]
pub struct ID([u8; ID::MAX_SIZE]);
impl ID {
pub const MAX_SIZE: usize = u128::BITS as usize / 8;
#[inline]
pub fn size(&self) -> usize {
Self::MAX_SIZE - (u128::from_le_bytes(self.0).leading_zeros() as usize / 8)
}
#[inline]
pub fn to_le_bytes(&self) -> [u8; Self::MAX_SIZE] {
self.0
}
#[inline]
pub fn rand() -> Self {
use rand::rngs::OsRng;
let id: u128 = OsRng.gen_range(1..u128::MAX);
Self(id.to_le_bytes())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SizeError(pub usize);
impl fmt::Display for SizeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0 == 0 {
write!(f, "0 is not a valid ID")
} else {
write!(
f,
"Maximum ID size of {} bytes exceeded: {} bytes",
ID::MAX_SIZE,
self.0
)
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for SizeError {}
macro_rules! impl_from_sized_slice_for_id {
($N: expr) => {
impl TryFrom<&[u8; $N]> for ID {
type Error = SizeError;
fn try_from(value: &[u8; $N]) -> Result<Self, Self::Error> {
let mut id = [0u8; ID::MAX_SIZE];
id[..$N].copy_from_slice(value);
let id = u128::from_le_bytes(id);
match NonZeroU128::new(id) {
Some(_) => Ok(Self(id.to_le_bytes())),
None => Err(SizeError(0)),
}
}
}
impl TryFrom<[u8; $N]> for ID {
type Error = SizeError;
fn try_from(id: [u8; $N]) -> Result<Self, Self::Error> {
(&id).try_into()
}
}
};
}
impl_from_sized_slice_for_id!(1);
impl_from_sized_slice_for_id!(2);
impl_from_sized_slice_for_id!(3);
impl_from_sized_slice_for_id!(4);
impl_from_sized_slice_for_id!(5);
impl_from_sized_slice_for_id!(6);
impl_from_sized_slice_for_id!(7);
impl_from_sized_slice_for_id!(8);
impl_from_sized_slice_for_id!(9);
impl_from_sized_slice_for_id!(10);
impl_from_sized_slice_for_id!(11);
impl_from_sized_slice_for_id!(12);
impl_from_sized_slice_for_id!(13);
impl_from_sized_slice_for_id!(14);
impl_from_sized_slice_for_id!(15);
impl_from_sized_slice_for_id!(16);
impl TryFrom<&[u8]> for ID {
type Error = SizeError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
let size = slice.len();
if size > Self::MAX_SIZE {
return Err(SizeError(size));
}
let mut id = [0u8; ID::MAX_SIZE];
id[..size].copy_from_slice(slice);
let id = u128::from_le_bytes(id);
match NonZeroU128::new(id) {
Some(_) => Ok(Self(id.to_le_bytes())),
None => Err(SizeError(0)),
}
}
}
impl TryFrom<u8> for ID {
type Error = SizeError;
fn try_from(id: u8) -> Result<Self, Self::Error> {
id.to_le_bytes().try_into()
}
}
impl From<NonZeroU8> for ID {
fn from(id: NonZeroU8) -> Self {
NonZeroU128::from(id).into()
}
}
impl TryFrom<u16> for ID {
type Error = SizeError;
fn try_from(id: u16) -> Result<Self, Self::Error> {
id.to_le_bytes().try_into()
}
}
impl From<NonZeroU16> for ID {
fn from(id: NonZeroU16) -> Self {
NonZeroU128::from(id).into()
}
}
impl TryFrom<u32> for ID {
type Error = SizeError;
fn try_from(id: u32) -> Result<Self, Self::Error> {
id.to_le_bytes().try_into()
}
}
impl From<NonZeroU32> for ID {
fn from(id: NonZeroU32) -> Self {
NonZeroU128::from(id).into()
}
}
impl TryFrom<u64> for ID {
type Error = SizeError;
fn try_from(id: u64) -> Result<Self, Self::Error> {
id.to_le_bytes().try_into()
}
}
impl From<NonZeroU64> for ID {
fn from(id: NonZeroU64) -> Self {
NonZeroU128::from(id).into()
}
}
impl TryFrom<u128> for ID {
type Error = SizeError;
fn try_from(id: u128) -> Result<Self, Self::Error> {
id.to_le_bytes().try_into()
}
}
impl From<NonZeroU128> for ID {
fn from(id: NonZeroU128) -> Self {
Self(id.get().to_le_bytes())
}
}
impl FromStr for ID {
type Err = ParseIDError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Err(ParseIDError::EmptyStringsNotValid);
}
if s.starts_with('0') {
return Err(ParseIDError::LeadingZeroNotValid);
}
let bs = u128::from_str_radix(s, 16).map_err(|_| ParseIDError::ParseIntError)?;
ID::try_from(bs).map_err(ParseIDError::SizeError)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ParseIDError {
EmptyStringsNotValid,
LeadingZeroNotValid,
ParseIntError,
SizeError(SizeError),
}
impl fmt::Display for ParseIDError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseIDError::EmptyStringsNotValid => write!(f, "Invalid ID (empty string)"),
ParseIDError::LeadingZeroNotValid => write!(f, "Invalid ID (leading with '0')"),
ParseIDError::ParseIntError => write!(f, "Invalid ID (not an unsigned integer)"),
ParseIDError::SizeError(e) => write!(f, "Invalid ID ({e})"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseIDError {}
impl fmt::Debug for ID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:x}", u128::from_le_bytes(self.0))
}
}
impl fmt::Display for ID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
mod tests {
#[cfg(feature = "std")]
#[test]
fn parse_display() {
let id = "1".parse::<crate::ID>().unwrap();
assert_eq!(id.to_string(), "1");
let id = "1bc0".parse::<crate::ID>().unwrap();
assert_eq!(id.to_string(), "1bc0");
let id = "ff00".parse::<crate::ID>().unwrap();
assert_eq!(id.to_string(), "ff00");
let id = "ff0".parse::<crate::ID>().unwrap();
assert_eq!(id.to_string(), "ff0");
let id = "abcd".parse::<crate::ID>().unwrap();
assert_eq!(id.to_string(), "abcd");
let id = "6bd9cb5f9f2644508fbbb0df1d6cce3a"
.parse::<crate::ID>()
.unwrap();
assert_eq!(id.to_string(), "6bd9cb5f9f2644508fbbb0df1d6cce3a");
"0".parse::<crate::ID>().unwrap_err();
"0bcd".parse::<crate::ID>().unwrap_err();
"6bd9cb5f9f2644508fbbb0df1d6cce3a0"
.parse::<crate::ID>()
.unwrap_err();
}
}