use crate::{HexSlice, hex_slice::HexDisplay};
use core::{array::TryFromSliceError, error, fmt, str::FromStr};
#[must_use]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct HexArray<const N: usize>(pub [u8; N]);
impl<const N: usize> Default for HexArray<N> {
fn default() -> Self {
Self([0u8; N])
}
}
impl<const N: usize> HexArray<N> {
#[inline]
pub const fn new(bytes: [u8; N]) -> Self {
Self(bytes)
}
#[inline]
#[must_use]
pub const fn into_inner(self) -> [u8; N] {
self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseHexError {
InvalidLength {
expected: usize,
actual: usize,
},
OddLength {
actual: usize,
},
InvalidHexCharacter {
c: char,
index: usize,
},
}
impl fmt::Display for ParseHexError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseHexError::InvalidLength { expected, actual } => {
write!(
f,
"expected {} hex characters, got {}",
expected, actual,
)
}
ParseHexError::OddLength { actual } => {
write!(
f,
"expected an even number of hex characters, got {actual}",
)
}
ParseHexError::InvalidHexCharacter { c, index } => {
write!(f, "invalid hex character '{}' at index {}", c, index,)
}
}
}
}
impl error::Error for ParseHexError {}
pub(crate) fn decode_hex_into(
s: &str,
out: &mut [u8],
) -> Result<(), ParseHexError> {
let expected = out.len() * 2;
if s.len() != expected {
return Err(ParseHexError::InvalidLength { expected, actual: s.len() });
}
hex::decode_to_slice(s, out).map_err(|e| {
match e {
hex::FromHexError::InvalidHexCharacter { c, index } => {
ParseHexError::InvalidHexCharacter { c, index }
}
hex::FromHexError::OddLength
| hex::FromHexError::InvalidStringLength => {
ParseHexError::InvalidLength { expected, actual: s.len() }
}
}
})
}
impl<const N: usize> FromStr for HexArray<N> {
type Err = ParseHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut out = [0u8; N];
decode_hex_into(s, &mut out)?;
Ok(Self(out))
}
}
impl<const N: usize> fmt::Debug for HexArray<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("HexArray").field(&HexDisplay(&self.0)).finish()
}
}
impl<const N: usize> fmt::Display for HexArray<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(HexSlice::new(&self.0), f)
}
}
impl<const N: usize> fmt::LowerHex for HexArray<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(HexSlice::new(&self.0), f)
}
}
impl<const N: usize> fmt::UpperHex for HexArray<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::UpperHex::fmt(HexSlice::new(&self.0), f)
}
}
impl<const N: usize> core::ops::Deref for HexArray<N> {
type Target = [u8; N];
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<const N: usize> core::ops::DerefMut for HexArray<N> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<const N: usize> AsRef<[u8]> for HexArray<N> {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl<const N: usize> AsMut<[u8]> for HexArray<N> {
#[inline]
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl<const N: usize> From<[u8; N]> for HexArray<N> {
#[inline]
fn from(bytes: [u8; N]) -> Self {
Self(bytes)
}
}
impl<const N: usize> From<HexArray<N>> for [u8; N] {
#[inline]
fn from(hex_array: HexArray<N>) -> Self {
hex_array.0
}
}
impl<const N: usize> TryFrom<&[u8]> for HexArray<N> {
type Error = TryFromSliceError;
#[inline]
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
<[u8; N]>::try_from(slice).map(Self)
}
}
#[cfg(feature = "serde")]
mod serde_impls {
use super::HexArray;
use crate::HexSlice;
use core::fmt;
use serde_core::{
Deserializer, Serialize,
de::{Expected, SeqAccess, Visitor},
};
struct HexExpected<const N: usize>;
impl<const N: usize> Expected for HexExpected<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "a byte array [u8; {}]", N)
}
}
struct HexStrExpected<const N: usize>;
impl<const N: usize> Expected for HexStrExpected<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "a hex string {} hex digits long", N * 2)
}
}
fn deserialize<'de, D, const N: usize>(
deserializer: D,
) -> Result<[u8; N], D::Error>
where
D: Deserializer<'de>,
{
use serde_core::de::Error;
if deserializer.is_human_readable() {
struct HexVisitor<const N: usize>;
impl<'de2, const N: usize> Visitor<'de2> for HexVisitor<N> {
type Value = [u8; N];
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "a hex string {} hex digits long", N * 2)
}
fn visit_str<E>(self, data: &str) -> Result<Self::Value, E>
where
E: Error,
{
let expected_len = N * 2;
if data.len() != expected_len {
return Err(E::invalid_length(
data.len(),
&HexStrExpected::<N>,
));
}
let mut out = [0u8; N];
hex::decode_to_slice(data, &mut out)
.map_err(Error::custom)?;
Ok(out)
}
}
deserializer.deserialize_str(HexVisitor)
} else {
struct BytesVisitor<const N: usize>;
impl<'de2, const N: usize> Visitor<'de2> for BytesVisitor<N> {
type Value = [u8; N];
fn expecting(
&self,
formatter: &mut fmt::Formatter,
) -> fmt::Result {
write!(formatter, "a byte array [u8; {}]", N)
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: Error,
{
v.try_into().map_err(|_| {
E::invalid_length(v.len(), &HexExpected::<N>)
})
}
fn visit_seq<A>(
self,
mut seq: A,
) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de2>,
{
if let Some(len) = seq.size_hint()
&& len != N
{
return Err(Error::invalid_length(
len,
&HexExpected::<N>,
));
}
let mut out = [0u8; N];
for (i, byte) in out.iter_mut().enumerate() {
*byte = seq.next_element()?.ok_or_else(|| {
Error::invalid_length(i, &HexExpected::<N>)
})?;
}
if seq.next_element::<u8>()?.is_some() {
return Err(Error::invalid_length(
N + 1,
&HexExpected::<N>,
));
}
Ok(out)
}
}
deserializer.deserialize_bytes(BytesVisitor)
}
}
impl<const N: usize> HexArray<N> {
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
pub fn serialize<S>(
bytes: &[u8; N],
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde_core::Serializer,
{
HexSlice::new(bytes).serialize(serializer)
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; N], D::Error>
where
D: Deserializer<'de>,
{
deserialize(deserializer)
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
impl<const N: usize> serde_core::Serialize for HexArray<N> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde_core::Serializer,
{
HexSlice::new(&self.0).serialize(serializer)
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
impl<'de, const N: usize> serde_core::Deserialize<'de> for HexArray<N> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserialize(deserializer).map(Self)
}
}
}
#[cfg(feature = "schemars08")]
mod schemars_impls {
use super::HexArray;
use crate::schemars_util::x_rust_type_extension;
use alloc::{boxed::Box, format, string::String};
use schemars08::{
JsonSchema,
r#gen::SchemaGenerator,
schema::{InstanceType, Schema, SchemaObject, StringValidation},
};
impl<const N: usize> JsonSchema for HexArray<N> {
fn schema_name() -> String {
format!("HexArray_{N}")
}
fn is_referenceable() -> bool {
false
}
fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
let hex_len = N * 2;
Schema::Object(SchemaObject {
instance_type: Some(InstanceType::String.into()),
string: Some(Box::new(StringValidation {
min_length: Some(hex_len as u32),
max_length: Some(hex_len as u32),
pattern: Some(format!("^[0-9a-fA-F]{{{hex_len}}}$")),
})),
extensions: x_rust_type_extension(&format!("HexArray::<{N}>")),
..Default::default()
})
}
}
}