use alloc::{
format,
string::{String, ToString as _},
};
use parity_scale_codec::{Decode, Encode, Error};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct InterfaceId(pub [u8; 8]);
impl InterfaceId {
pub const fn zero() -> Self {
Self([0u8; 8])
}
pub const fn from_bytes_32(bytes: [u8; 32]) -> Self {
let inner = [
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
];
Self(inner)
}
pub const fn from_bytes_8(bytes: [u8; 8]) -> Self {
Self(bytes)
}
pub const fn from_u64(int: u64) -> Self {
Self(int.to_be_bytes())
}
pub const fn as_u64(&self) -> u64 {
u64::from_be_bytes(self.0)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn try_read_bytes(bytes: &mut &[u8]) -> Result<Self, &'static str> {
if bytes.len() < 8 {
return Err("Insufficient bytes for interface ID");
}
let mut id = [0u8; 8];
id.copy_from_slice(&bytes[0..8]);
*bytes = &bytes[8..];
Ok(Self(id))
}
pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
let mut slice = bytes;
Self::try_read_bytes(&mut slice)
}
}
impl core::fmt::Debug for InterfaceId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(self, f)
}
}
impl core::fmt::Display for InterfaceId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("0x")?;
for byte in self.as_bytes() {
write!(f, "{byte:02x}")?;
}
Ok(())
}
}
impl core::str::FromStr for InterfaceId {
type Err = String;
fn from_str(mut s: &str) -> Result<Self, Self::Err> {
if let Some(rest) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
s = rest;
}
if s.len() != 16 {
return Err(format!("expected 16 hex digits (8 bytes), got {}", s.len()));
}
let mut bytes = [0u8; 8];
for (i, chunk) in s.as_bytes().chunks_exact(2).enumerate() {
let hex = core::str::from_utf8(chunk).map_err(|_| "invalid UTF-8".to_string())?;
bytes[i] =
u8::from_str_radix(hex, 16).map_err(|_| format!("invalid hex byte: {hex}"))?;
}
Ok(InterfaceId(bytes))
}
}
impl Encode for InterfaceId {
fn encode_to<O: parity_scale_codec::Output + ?Sized>(&self, dest: &mut O) {
dest.write(self.as_bytes());
}
}
impl Decode for InterfaceId {
fn decode<I: parity_scale_codec::Input>(input: &mut I) -> Result<Self, Error> {
let mut bytes = [0u8; 8];
input.read(&mut bytes)?;
Ok(Self(bytes))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn interface_id_codec() {
let inner = [1u8, 2, 3, 4, 5, 6, 7, 8];
let id = InterfaceId(inner);
let encoded = id.encode();
assert_eq!(inner.encode(), encoded);
let decoded = Decode::decode(&mut &encoded[..]).unwrap();
assert_eq!(id, decoded);
}
#[test]
fn interface_id_serde() {
let inner = [1u8, 2, 3, 4, 5, 6, 7, 8];
let mut slice = inner.as_slice();
let id = InterfaceId::try_read_bytes(&mut slice).unwrap();
assert_eq!(inner, id.0);
assert_eq!(slice.len(), 0);
assert_eq!(id.as_bytes(), inner);
}
#[test]
fn interface_id_try_read_bytes() {
let data = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let mut slice = data.as_slice();
let id = InterfaceId::try_read_bytes(&mut slice).unwrap();
assert_eq!(id.0, [1, 2, 3, 4, 5, 6, 7, 8]);
assert_eq!(slice, &[9, 10]);
let data = [1u8, 2, 3, 4, 5, 6, 7];
let mut slice = data.as_slice();
let result = InterfaceId::try_read_bytes(&mut slice);
assert_eq!(result, Err("Insufficient bytes for interface ID"));
}
#[test]
fn interface_id_try_from_bytes() {
let data = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let slice = data.as_slice();
let id = InterfaceId::try_from_bytes(slice).unwrap();
assert_eq!(id.0, [1, 2, 3, 4, 5, 6, 7, 8]);
assert_eq!(slice.len(), data.len()); }
}