1use crate::{HexSlice, hex_slice::HexDisplay};
7use core::{array::TryFromSliceError, error, fmt, str::FromStr};
8
9#[must_use]
33#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
34pub struct HexArray<const N: usize>(pub [u8; N]);
35
36impl<const N: usize> Default for HexArray<N> {
37 fn default() -> Self {
38 Self([0u8; N])
39 }
40}
41
42impl<const N: usize> HexArray<N> {
43 #[inline]
45 pub const fn new(bytes: [u8; N]) -> Self {
46 Self(bytes)
47 }
48
49 #[inline]
51 #[must_use]
52 pub const fn into_inner(self) -> [u8; N] {
53 self.0
54 }
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
60pub enum ParseHexError {
61 InvalidLength {
63 expected: usize,
65 actual: usize,
67 },
68 OddLength {
70 actual: usize,
72 },
73 InvalidHexCharacter {
75 c: char,
77 index: usize,
79 },
80}
81
82impl fmt::Display for ParseHexError {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 match self {
85 ParseHexError::InvalidLength { expected, actual } => {
86 write!(
87 f,
88 "expected {} hex characters, got {}",
89 expected, actual,
90 )
91 }
92 ParseHexError::OddLength { actual } => {
93 write!(
94 f,
95 "expected an even number of hex characters, got {actual}",
96 )
97 }
98 ParseHexError::InvalidHexCharacter { c, index } => {
99 write!(f, "invalid hex character '{}' at index {}", c, index,)
100 }
101 }
102 }
103}
104
105impl error::Error for ParseHexError {}
106
107pub(crate) fn decode_hex_into(
110 s: &str,
111 out: &mut [u8],
112) -> Result<(), ParseHexError> {
113 let expected = out.len() * 2;
114 if s.len() != expected {
115 return Err(ParseHexError::InvalidLength { expected, actual: s.len() });
116 }
117 hex::decode_to_slice(s, out).map_err(|e| {
118 match e {
119 hex::FromHexError::InvalidHexCharacter { c, index } => {
120 ParseHexError::InvalidHexCharacter { c, index }
121 }
122 hex::FromHexError::OddLength
125 | hex::FromHexError::InvalidStringLength => {
126 ParseHexError::InvalidLength { expected, actual: s.len() }
127 }
128 }
129 })
130}
131
132impl<const N: usize> FromStr for HexArray<N> {
133 type Err = ParseHexError;
134
135 fn from_str(s: &str) -> Result<Self, Self::Err> {
136 let mut out = [0u8; N];
137 decode_hex_into(s, &mut out)?;
138 Ok(Self(out))
139 }
140}
141
142impl<const N: usize> fmt::Debug for HexArray<N> {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 f.debug_tuple("HexArray").field(&HexDisplay(&self.0)).finish()
145 }
146}
147
148impl<const N: usize> fmt::Display for HexArray<N> {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 fmt::Display::fmt(HexSlice::new(&self.0), f)
151 }
152}
153
154impl<const N: usize> fmt::LowerHex for HexArray<N> {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 fmt::LowerHex::fmt(HexSlice::new(&self.0), f)
157 }
158}
159
160impl<const N: usize> fmt::UpperHex for HexArray<N> {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 fmt::UpperHex::fmt(HexSlice::new(&self.0), f)
163 }
164}
165
166impl<const N: usize> core::ops::Deref for HexArray<N> {
167 type Target = [u8; N];
168
169 #[inline]
170 fn deref(&self) -> &Self::Target {
171 &self.0
172 }
173}
174
175impl<const N: usize> core::ops::DerefMut for HexArray<N> {
176 #[inline]
177 fn deref_mut(&mut self) -> &mut Self::Target {
178 &mut self.0
179 }
180}
181
182impl<const N: usize> AsRef<[u8]> for HexArray<N> {
183 #[inline]
184 fn as_ref(&self) -> &[u8] {
185 &self.0
186 }
187}
188
189impl<const N: usize> AsMut<[u8]> for HexArray<N> {
190 #[inline]
191 fn as_mut(&mut self) -> &mut [u8] {
192 &mut self.0
193 }
194}
195
196impl<const N: usize> From<[u8; N]> for HexArray<N> {
197 #[inline]
198 fn from(bytes: [u8; N]) -> Self {
199 Self(bytes)
200 }
201}
202
203impl<const N: usize> From<HexArray<N>> for [u8; N] {
204 #[inline]
205 fn from(hex_array: HexArray<N>) -> Self {
206 hex_array.0
207 }
208}
209
210impl<const N: usize> TryFrom<&[u8]> for HexArray<N> {
211 type Error = TryFromSliceError;
212
213 #[inline]
214 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
215 <[u8; N]>::try_from(slice).map(Self)
216 }
217}
218
219#[cfg(feature = "serde")]
220mod serde_impls {
221 use super::HexArray;
222 use crate::HexSlice;
223 use core::fmt;
224 use serde_core::{
225 Deserializer, Serialize,
226 de::{Expected, SeqAccess, Visitor},
227 };
228
229 struct HexExpected<const N: usize>;
230
231 impl<const N: usize> Expected for HexExpected<N> {
232 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
233 write!(f, "a byte array [u8; {}]", N)
234 }
235 }
236
237 struct HexStrExpected<const N: usize>;
238
239 impl<const N: usize> Expected for HexStrExpected<N> {
240 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
241 write!(f, "a hex string {} hex digits long", N * 2)
242 }
243 }
244
245 fn deserialize<'de, D, const N: usize>(
248 deserializer: D,
249 ) -> Result<[u8; N], D::Error>
250 where
251 D: Deserializer<'de>,
252 {
253 use serde_core::de::Error;
254
255 if deserializer.is_human_readable() {
256 struct HexVisitor<const N: usize>;
259
260 impl<'de2, const N: usize> Visitor<'de2> for HexVisitor<N> {
261 type Value = [u8; N];
262
263 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
264 write!(f, "a hex string {} hex digits long", N * 2)
265 }
266
267 fn visit_str<E>(self, data: &str) -> Result<Self::Value, E>
268 where
269 E: Error,
270 {
271 let expected_len = N * 2;
272 if data.len() != expected_len {
273 return Err(E::invalid_length(
274 data.len(),
275 &HexStrExpected::<N>,
276 ));
277 }
278 let mut out = [0u8; N];
279 hex::decode_to_slice(data, &mut out)
280 .map_err(Error::custom)?;
281 Ok(out)
282 }
283 }
284
285 deserializer.deserialize_str(HexVisitor)
286 } else {
287 struct BytesVisitor<const N: usize>;
288
289 impl<'de2, const N: usize> Visitor<'de2> for BytesVisitor<N> {
290 type Value = [u8; N];
291
292 fn expecting(
293 &self,
294 formatter: &mut fmt::Formatter,
295 ) -> fmt::Result {
296 write!(formatter, "a byte array [u8; {}]", N)
297 }
298
299 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
300 where
301 E: Error,
302 {
303 v.try_into().map_err(|_| {
304 E::invalid_length(v.len(), &HexExpected::<N>)
305 })
306 }
307
308 fn visit_seq<A>(
309 self,
310 mut seq: A,
311 ) -> Result<Self::Value, A::Error>
312 where
313 A: SeqAccess<'de2>,
314 {
315 if let Some(len) = seq.size_hint()
318 && len != N
319 {
320 return Err(Error::invalid_length(
321 len,
322 &HexExpected::<N>,
323 ));
324 }
325 let mut out = [0u8; N];
326 for (i, byte) in out.iter_mut().enumerate() {
327 *byte = seq.next_element()?.ok_or_else(|| {
328 Error::invalid_length(i, &HexExpected::<N>)
329 })?;
330 }
331 if seq.next_element::<u8>()?.is_some() {
334 return Err(Error::invalid_length(
337 N + 1,
338 &HexExpected::<N>,
339 ));
340 }
341 Ok(out)
342 }
343 }
344
345 deserializer.deserialize_bytes(BytesVisitor)
346 }
347 }
348
349 impl<const N: usize> HexArray<N> {
350 #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
373 pub fn serialize<S>(
374 bytes: &[u8; N],
375 serializer: S,
376 ) -> Result<S::Ok, S::Error>
377 where
378 S: serde_core::Serializer,
379 {
380 HexSlice::new(bytes).serialize(serializer)
381 }
382
383 #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
405 pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; N], D::Error>
406 where
407 D: Deserializer<'de>,
408 {
409 deserialize(deserializer)
410 }
411 }
412
413 #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
414 impl<const N: usize> serde_core::Serialize for HexArray<N> {
415 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
416 where
417 S: serde_core::Serializer,
418 {
419 HexSlice::new(&self.0).serialize(serializer)
420 }
421 }
422
423 #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
424 impl<'de, const N: usize> serde_core::Deserialize<'de> for HexArray<N> {
425 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
426 where
427 D: Deserializer<'de>,
428 {
429 deserialize(deserializer).map(Self)
430 }
431 }
432}
433
434#[cfg(feature = "schemars08")]
435mod schemars_impls {
436 use super::HexArray;
437 use crate::schemars_util::x_rust_type_extension;
438 use alloc::{boxed::Box, format, string::String};
439 use schemars08::{
440 JsonSchema,
441 r#gen::SchemaGenerator,
442 schema::{InstanceType, Schema, SchemaObject, StringValidation},
443 };
444
445 impl<const N: usize> JsonSchema for HexArray<N> {
446 fn schema_name() -> String {
447 format!("HexArray_{N}")
448 }
449
450 fn is_referenceable() -> bool {
451 false
452 }
453
454 fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
455 let hex_len = N * 2;
456 Schema::Object(SchemaObject {
457 instance_type: Some(InstanceType::String.into()),
458 string: Some(Box::new(StringValidation {
459 min_length: Some(hex_len as u32),
460 max_length: Some(hex_len as u32),
461 pattern: Some(format!("^[0-9a-fA-F]{{{hex_len}}}$")),
462 })),
463 extensions: x_rust_type_extension(&format!("HexArray::<{N}>")),
464 ..Default::default()
465 })
466 }
467 }
468}