1use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use std::fmt;
8
9#[derive(Clone, Copy, PartialEq, Eq, Hash)]
24pub struct FixedBytes<const N: usize> {
25 bytes: [u8; N],
26}
27
28impl<const N: usize> FixedBytes<N> {
29 #[must_use]
31 pub const fn new(bytes: [u8; N]) -> Self {
32 Self { bytes }
33 }
34
35 #[must_use]
45 pub fn from_slice(slice: &[u8]) -> Option<Self> {
46 if slice.len() == N {
47 let mut bytes = [0u8; N];
48 bytes.copy_from_slice(slice);
49 Some(Self { bytes })
50 } else {
51 None
52 }
53 }
54
55 pub fn from_hex(hex: &str) -> Result<Self, String> {
60 if hex.len() != N * 2 {
61 return Err(format!(
62 "Invalid hex length: expected {} chars, got {}",
63 N * 2,
64 hex.len()
65 ));
66 }
67
68 let mut bytes = [0u8; N];
69 for i in 0..N {
70 let byte_str = &hex[i * 2..i * 2 + 2];
71 bytes[i] = u8::from_str_radix(byte_str, 16)
72 .map_err(|e| format!("Invalid hex character: {e}"))?;
73 }
74 Ok(Self { bytes })
75 }
76
77 #[must_use]
79 pub const fn as_bytes(&self) -> &[u8; N] {
80 &self.bytes
81 }
82
83 #[must_use]
85 pub fn as_bytes_mut(&mut self) -> &mut [u8; N] {
86 &mut self.bytes
87 }
88
89 #[must_use]
91 pub fn to_hex(&self) -> String {
92 self.bytes
93 .iter()
94 .map(|b| format!("{b:02x}"))
95 .collect::<String>()
96 }
97
98 #[must_use]
100 pub const fn size() -> usize {
101 N
102 }
103
104 #[must_use]
106 pub fn is_zero(&self) -> bool {
107 self.bytes.iter().all(|&b| b == 0)
108 }
109
110 #[must_use]
112 pub const fn zero() -> Self {
113 Self { bytes: [0u8; N] }
114 }
115}
116
117impl<const N: usize> Default for FixedBytes<N> {
118 fn default() -> Self {
119 Self::zero()
120 }
121}
122
123impl<const N: usize> AsRef<[u8]> for FixedBytes<N> {
124 fn as_ref(&self) -> &[u8] {
125 &self.bytes
126 }
127}
128
129impl<const N: usize> AsMut<[u8]> for FixedBytes<N> {
130 fn as_mut(&mut self) -> &mut [u8] {
131 &mut self.bytes
132 }
133}
134
135impl<const N: usize> From<[u8; N]> for FixedBytes<N> {
136 fn from(bytes: [u8; N]) -> Self {
137 Self::new(bytes)
138 }
139}
140
141impl<const N: usize> From<FixedBytes<N>> for [u8; N] {
142 fn from(fixed: FixedBytes<N>) -> Self {
143 fixed.bytes
144 }
145}
146
147impl<const N: usize> fmt::Debug for FixedBytes<N> {
148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 write!(f, "FixedBytes<{}>({})", N, self.to_hex())
150 }
151}
152
153impl<const N: usize> fmt::Display for FixedBytes<N> {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 write!(f, "{}", self.to_hex())
156 }
157}
158
159impl<const N: usize> Serialize for FixedBytes<N> {
161 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
162 where
163 S: Serializer,
164 {
165 serializer.serialize_bytes(&self.bytes)
167 }
168}
169
170impl<'de, const N: usize> Deserialize<'de> for FixedBytes<N> {
171 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
172 where
173 D: Deserializer<'de>,
174 {
175 let bytes: Vec<u8> = serde::de::Deserialize::deserialize(deserializer)?;
177 if bytes.len() != N {
178 return Err(serde::de::Error::custom(format!(
179 "expected {} bytes, got {}",
180 N,
181 bytes.len()
182 )));
183 }
184 let mut array = [0u8; N];
185 array.copy_from_slice(&bytes);
186 Ok(Self { bytes: array })
187 }
188}
189
190pub type Blake3Hash = FixedBytes<32>;
194
195pub type Ed25519Signature = FixedBytes<64>;
197
198pub type Ed25519PublicKey = FixedBytes<32>;
200
201pub type Nonce32 = FixedBytes<32>;
203
204pub type Sha256Hash = FixedBytes<32>;
206
207pub type Sha512Hash = FixedBytes<64>;
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn test_fixed_bytes_new() {
216 let bytes = [1u8; 32];
217 let fixed = FixedBytes::<32>::new(bytes);
218 assert_eq!(fixed.as_bytes(), &bytes);
219 }
220
221 #[test]
222 fn test_fixed_bytes_from_slice() {
223 let slice = &[1, 2, 3, 4];
224 let fixed = FixedBytes::<4>::from_slice(slice).unwrap();
225 assert_eq!(fixed.as_bytes(), &[1, 2, 3, 4]);
226
227 let wrong = FixedBytes::<8>::from_slice(slice);
229 assert!(wrong.is_none());
230 }
231
232 #[test]
233 fn test_fixed_bytes_hex_roundtrip() {
234 let bytes = [0x12, 0x34, 0xAB, 0xCD];
235 let fixed = FixedBytes::<4>::new(bytes);
236 let hex = fixed.to_hex();
237 assert_eq!(hex, "1234abcd");
238
239 let decoded = FixedBytes::<4>::from_hex(&hex).unwrap();
240 assert_eq!(decoded, fixed);
241 }
242
243 #[test]
244 fn test_fixed_bytes_from_hex_errors() {
245 let result = FixedBytes::<4>::from_hex("12345");
247 assert!(result.is_err());
248
249 let result = FixedBytes::<4>::from_hex("1234567g");
251 assert!(result.is_err());
252 }
253
254 #[test]
255 fn test_fixed_bytes_size() {
256 assert_eq!(FixedBytes::<32>::size(), 32);
257 assert_eq!(FixedBytes::<64>::size(), 64);
258 assert_eq!(Blake3Hash::size(), 32);
259 assert_eq!(Ed25519Signature::size(), 64);
260 }
261
262 #[test]
263 fn test_fixed_bytes_is_zero() {
264 let zero = FixedBytes::<8>::zero();
265 assert!(zero.is_zero());
266
267 let mut non_zero = FixedBytes::<8>::zero();
268 non_zero.as_bytes_mut()[0] = 1;
269 assert!(!non_zero.is_zero());
270 }
271
272 #[test]
273 fn test_fixed_bytes_default() {
274 let default = FixedBytes::<16>::default();
275 assert!(default.is_zero());
276 }
277
278 #[test]
279 fn test_fixed_bytes_display() {
280 let fixed = FixedBytes::<4>::new([0xAA, 0xBB, 0xCC, 0xDD]);
281 let display = format!("{fixed}");
282 assert_eq!(display, "aabbccdd");
283 }
284
285 #[test]
286 fn test_fixed_bytes_debug() {
287 let fixed = FixedBytes::<4>::new([0xAA, 0xBB, 0xCC, 0xDD]);
288 let debug = format!("{fixed:?}");
289 assert_eq!(debug, "FixedBytes<4>(aabbccdd)");
290 }
291
292 #[test]
293 fn test_type_aliases() {
294 let _hash: Blake3Hash = FixedBytes::zero();
295 let _sig: Ed25519Signature = FixedBytes::zero();
296 let _pubkey: Ed25519PublicKey = FixedBytes::zero();
297 let _nonce: Nonce32 = FixedBytes::zero();
298 let _sha256: Sha256Hash = FixedBytes::zero();
299 let _sha512: Sha512Hash = FixedBytes::zero();
300
301 assert_eq!(Blake3Hash::size(), 32);
303 assert_eq!(Ed25519Signature::size(), 64);
304 assert_eq!(Ed25519PublicKey::size(), 32);
305 assert_eq!(Nonce32::size(), 32);
306 assert_eq!(Sha256Hash::size(), 32);
307 assert_eq!(Sha512Hash::size(), 64);
308 }
309
310 #[test]
311 fn test_fixed_bytes_equality() {
312 let a = FixedBytes::<8>::new([1, 2, 3, 4, 5, 6, 7, 8]);
313 let b = FixedBytes::<8>::new([1, 2, 3, 4, 5, 6, 7, 8]);
314 let c = FixedBytes::<8>::new([1, 2, 3, 4, 5, 6, 7, 9]);
315
316 assert_eq!(a, b);
317 assert_ne!(a, c);
318 }
319
320 #[test]
321 fn test_fixed_bytes_conversions() {
322 let array = [1u8, 2, 3, 4];
323 let fixed: FixedBytes<4> = array.into();
324 let back: [u8; 4] = fixed.into();
325 assert_eq!(array, back);
326 }
327
328 #[test]
329 fn test_fixed_bytes_as_ref() {
330 let fixed = FixedBytes::<4>::new([1, 2, 3, 4]);
331 let slice: &[u8] = fixed.as_ref();
332 assert_eq!(slice, &[1, 2, 3, 4]);
333 }
334
335 #[test]
336 fn test_fixed_bytes_serde() {
337 let fixed = FixedBytes::<8>::new([1, 2, 3, 4, 5, 6, 7, 8]);
338 let json = serde_json::to_string(&fixed).unwrap();
339 let decoded: FixedBytes<8> = serde_json::from_str(&json).unwrap();
340 assert_eq!(fixed, decoded);
341 }
342}