use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{self, Debug, Display, Formatter};
use thiserror::Error;
use utoipa::ToSchema;
#[derive(Clone, PartialEq, Eq, Hash, Copy, ToSchema)]
pub struct ByteArray<const N: usize>([u8; N]);
impl<const N: usize> ByteArray<N> {
pub fn new(data: [u8; N]) -> Self {
ByteArray(data)
}
pub fn as_slice(&self) -> &[u8] {
&self.0
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.0
}
pub fn into_inner(self) -> [u8; N] {
self.0
}
pub fn len(&self) -> usize {
N
}
pub fn is_empty(&self) -> bool {
N == 0
}
}
impl<const N: usize> TryFrom<Vec<u8>> for ByteArray<N> {
type Error = ByteArrayError<N>;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
if value.len() != N {
return Err(ByteArrayError { length: N });
}
let mut array = [0u8; N];
array.copy_from_slice(&value);
Ok(ByteArray(array))
}
}
impl<const N: usize> Display for ByteArray<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let hex_string = const_hex::encode(self.0);
write!(f, "0x{hex_string}")
}
}
impl<const N: usize> Debug for ByteArray<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<const N: usize> Serialize for ByteArray<N> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let hex_string = self.to_string();
serializer.serialize_str(&hex_string)
}
}
impl<'de, const N: usize> Deserialize<'de> for ByteArray<N> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let bytes = const_hex::decode(&s).map_err(|e| serde::de::Error::custom(e.to_string()))?;
let array: [u8; N] = bytes
.try_into()
.map_err(|_| serde::de::Error::custom("invalid hex length"))?;
Ok(ByteArray(array))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_display() {
let data = ByteArray::<4>([0xDE, 0xAD, 0xBE, 0xEF]);
assert_eq!(format!("{data}"), "0xdeadbeef");
}
#[test]
fn test_serde() {
let data = ByteArray::<4>([0xDE, 0xAD, 0xBE, 0xEF]);
let serialized = serde_json::to_string(&data).unwrap();
assert_eq!(serialized, "\"0xdeadbeef\"");
let deserialized: ByteArray<4> = serde_json::from_str("\"0xdeadbeef\"").unwrap();
assert_eq!(deserialized.0, [0xDE, 0xAD, 0xBE, 0xEF]);
}
#[test]
fn test_debug() {
let data = ByteArray::<4>([0xDE, 0xAD, 0xBE, 0xEF]);
assert_eq!(format!("{data:?}"), "0xdeadbeef");
assert_eq!(format!("{data:#?}"), "0xdeadbeef");
}
}
#[derive(Error, Debug)]
#[error("ByteArray<{N}> must be exactly {N} bytes long")]
pub struct ByteArrayError<const N: usize> {
pub length: usize,
}