Skip to main content

sails_idl_ast/
interface_id.rs

1use alloc::{
2    format,
3    string::{String, ToString as _},
4};
5use parity_scale_codec::{Decode, Encode, Error};
6
7/// Unique identifier for a service (or "interface" in terms of sails binary protocol).
8///
9/// For more information about interface IDs, see the interface ID spec.
10#[derive(Clone, Copy, PartialEq, Eq)]
11pub struct InterfaceId(pub [u8; 8]);
12
13impl InterfaceId {
14    /// Create a zeroed interface ID.
15    pub const fn zero() -> Self {
16        Self([0u8; 8])
17    }
18
19    /// Create interface ID from bytes.
20    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    /// Create interface ID from bytes.
29    pub const fn from_bytes_8(bytes: [u8; 8]) -> Self {
30        Self(bytes)
31    }
32
33    /// Create interface ID from u64.
34    pub const fn from_u64(int: u64) -> Self {
35        Self(int.to_be_bytes())
36    }
37
38    /// Get interface ID as a u64.
39    pub const fn as_u64(&self) -> u64 {
40        u64::from_be_bytes(self.0)
41    }
42
43    /// Get interface ID as a byte slice
44    pub fn as_bytes(&self) -> &[u8] {
45        &self.0
46    }
47
48    /// Deserialize from bytes, advancing the slice
49    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    /// Deserialize from bytes without mutating the input
61    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        // Strip optional 0x / 0X prefix
88        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        // Read from a slice with extra data
151        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        // Read from a slice with insufficient data
159        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()); // Original slice should remain unchanged
173    }
174}