use std::fmt;
use crate::Error;
pub trait FixedField<'a>: Sized {
const LENGTH: usize;
fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error>;
}
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Alphanumeric<'a, const N: usize>(pub(super) &'a [u8; N]);
impl<'a, const N: usize> Alphanumeric<'a, N> {
#[inline]
pub fn as_bytes(&self) -> &'a [u8] {
self.0
}
#[inline]
pub fn as_str(&self) -> &'a str {
self.as_raw_str().trim_end()
}
#[inline]
pub fn as_raw_str(&self) -> &'a str {
std::str::from_utf8(self.0).unwrap_or("")
}
#[inline]
pub fn is_blank(&self) -> bool {
self.0.iter().all(|&b| b == b' ')
}
#[inline]
pub fn first(&self) -> u8 {
self.0[0]
}
}
impl<'a, const N: usize> FixedField<'a> for Alphanumeric<'a, N> {
const LENGTH: usize = N;
fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
if bytes.len() < N {
return Err(Error::InvalidFieldLength {
expected: N,
actual: bytes.len(),
});
}
let arr = unsafe { &*(bytes.as_ptr() as *const [u8; N]) };
Ok(Self(arr))
}
}
impl<const N: usize> fmt::Debug for Alphanumeric<'_, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"{}\"", self.as_str())
}
}
impl<const N: usize> fmt::Display for Alphanumeric<'_, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl<const N: usize> From<Alphanumeric<'_, N>> for String {
fn from(a: Alphanumeric<'_, N>) -> Self {
a.as_str().to_owned()
}
}
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Numeric<'a, const N: usize>(&'a [u8; N]);
macro_rules! numeric_impl {
($n:tt => $($method:ident : $t:ty),+) => {
impl<'a> Numeric<'a, $n> {
$(
#[inline]
pub fn $method(&self) -> Result<$t, Error> {
parse_numeric!($n, $t, self.0)
}
)+
}
};
}
numeric_impl!(1 => as_u8: u8);
numeric_impl!(2 => as_u8: u8, as_u16: u16);
numeric_impl!(3 => as_u8: u8, as_u16: u16);
numeric_impl!(4 => as_u16: u16, as_u32: u32);
numeric_impl!(5 => as_u32: u32);
impl<'a, const N: usize> Numeric<'a, N> {
#[inline]
pub fn is_blank(&self) -> bool {
self.0.iter().all(|&b| b == b' ')
}
}
impl<'a, const N: usize> FixedField<'a> for Numeric<'a, N> {
const LENGTH: usize = N;
fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
if bytes.len() < N {
return Err(Error::InvalidFieldLength {
expected: N,
actual: bytes.len(),
});
}
let arr = unsafe { &*(bytes.as_ptr() as *const [u8; N]) };
Ok(Self(arr))
}
}
impl<const N: usize> fmt::Debug for Numeric<'_, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = String::from_utf8_lossy(self.0);
write!(f, "{s}")
}
}
impl<'a, T> FixedField<'a> for Option<T>
where
T: FixedField<'a>,
{
const LENGTH: usize = T::LENGTH;
fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
if bytes
.get(..T::LENGTH)
.is_some_and(|b| b.iter().all(|&c| c == b' '))
{
Ok(None)
} else {
T::from_bytes(bytes).map(Some)
}
}
}