1use alloc::{
2 format,
3 string::{String, ToString as _},
4};
5use parity_scale_codec::{Decode, Encode, Error};
6
7#[derive(Clone, Copy, PartialEq, Eq)]
11pub struct InterfaceId(pub [u8; 8]);
12
13impl InterfaceId {
14 pub const fn zero() -> Self {
16 Self([0u8; 8])
17 }
18
19 pub const fn from_bytes_32(bytes: [u8; 32]) -> Self {
21 let inner = [
22 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
23 ];
24
25 Self(inner)
26 }
27
28 pub const fn from_bytes_8(bytes: [u8; 8]) -> Self {
30 Self(bytes)
31 }
32
33 pub const fn from_u64(int: u64) -> Self {
35 Self(int.to_be_bytes())
36 }
37
38 pub const fn as_u64(&self) -> u64 {
40 u64::from_be_bytes(self.0)
41 }
42
43 pub fn as_bytes(&self) -> &[u8] {
45 &self.0
46 }
47
48 pub fn try_read_bytes(bytes: &mut &[u8]) -> Result<Self, &'static str> {
50 if bytes.len() < 8 {
51 return Err("Insufficient bytes for interface ID");
52 }
53
54 let mut id = [0u8; 8];
55 id.copy_from_slice(&bytes[0..8]);
56 *bytes = &bytes[8..];
57 Ok(Self(id))
58 }
59
60 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
62 let mut slice = bytes;
63 Self::try_read_bytes(&mut slice)
64 }
65}
66
67impl core::fmt::Debug for InterfaceId {
68 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69 core::fmt::Display::fmt(self, f)
70 }
71}
72
73impl core::fmt::Display for InterfaceId {
74 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
75 f.write_str("0x")?;
76 for byte in self.as_bytes() {
77 write!(f, "{byte:02x}")?;
78 }
79 Ok(())
80 }
81}
82
83impl core::str::FromStr for InterfaceId {
84 type Err = String;
85
86 fn from_str(mut s: &str) -> Result<Self, Self::Err> {
87 if let Some(rest) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
89 s = rest;
90 }
91
92 if s.len() != 16 {
93 return Err(format!("expected 16 hex digits (8 bytes), got {}", s.len()));
94 }
95
96 let mut bytes = [0u8; 8];
97 for (i, chunk) in s.as_bytes().chunks_exact(2).enumerate() {
98 let hex = core::str::from_utf8(chunk).map_err(|_| "invalid UTF-8".to_string())?;
99
100 bytes[i] =
101 u8::from_str_radix(hex, 16).map_err(|_| format!("invalid hex byte: {hex}"))?;
102 }
103
104 Ok(InterfaceId(bytes))
105 }
106}
107
108impl Encode for InterfaceId {
109 fn encode_to<O: parity_scale_codec::Output + ?Sized>(&self, dest: &mut O) {
110 dest.write(self.as_bytes());
111 }
112}
113
114impl Decode for InterfaceId {
115 fn decode<I: parity_scale_codec::Input>(input: &mut I) -> Result<Self, Error> {
116 let mut bytes = [0u8; 8];
117 input.read(&mut bytes)?;
118 Ok(Self(bytes))
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn interface_id_codec() {
128 let inner = [1u8, 2, 3, 4, 5, 6, 7, 8];
129 let id = InterfaceId(inner);
130
131 let encoded = id.encode();
132 assert_eq!(inner.encode(), encoded);
133
134 let decoded = Decode::decode(&mut &encoded[..]).unwrap();
135 assert_eq!(id, decoded);
136 }
137
138 #[test]
139 fn interface_id_serde() {
140 let inner = [1u8, 2, 3, 4, 5, 6, 7, 8];
141 let mut slice = inner.as_slice();
142 let id = InterfaceId::try_read_bytes(&mut slice).unwrap();
143 assert_eq!(inner, id.0);
144 assert_eq!(slice.len(), 0);
145 assert_eq!(id.as_bytes(), inner);
146 }
147
148 #[test]
149 fn interface_id_try_read_bytes() {
150 let data = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10];
152 let mut slice = data.as_slice();
153
154 let id = InterfaceId::try_read_bytes(&mut slice).unwrap();
155 assert_eq!(id.0, [1, 2, 3, 4, 5, 6, 7, 8]);
156 assert_eq!(slice, &[9, 10]);
157
158 let data = [1u8, 2, 3, 4, 5, 6, 7];
160 let mut slice = data.as_slice();
161 let result = InterfaceId::try_read_bytes(&mut slice);
162 assert_eq!(result, Err("Insufficient bytes for interface ID"));
163 }
164
165 #[test]
166 fn interface_id_try_from_bytes() {
167 let data = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10];
168 let slice = data.as_slice();
169
170 let id = InterfaceId::try_from_bytes(slice).unwrap();
171 assert_eq!(id.0, [1, 2, 3, 4, 5, 6, 7, 8]);
172 assert_eq!(slice.len(), data.len()); }
174}