use super::error::DigitsParseError;
use core::ops::{Index, Range};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Digits<const L: usize> {
inner: [u8; L],
}
impl<const L: usize> Digits<L> {
pub fn new(bytes: &[u8; L]) -> Option<Self> {
if bytes.iter().any(|v| *v > 9) {
None
} else {
Some(unsafe { Self::new_unchecked(bytes) })
}
}
#[inline]
pub unsafe fn new_unchecked(bytes: &[u8]) -> Self {
let mut result = Self { inner: [0; L] };
result.inner.copy_from_slice(&bytes[..L]);
result
}
#[inline]
pub const fn as_bytes(&self) -> &[u8; L] {
&self.inner
}
#[inline]
pub fn as_ascii_bytes(&self) -> [u8; L] {
let mut ascii_bytes = self.inner.clone();
for byte in ascii_bytes.iter_mut() {
*byte += b'0';
}
ascii_bytes
}
#[inline]
pub fn get(&self, idx: usize) -> Option<&u8> {
self.inner.get(idx)
}
#[inline]
pub const fn to_bytes(self) -> [u8; L] {
self.inner
}
#[inline]
pub fn to_ascii_bytes(mut self) -> [u8; L] {
for byte in self.inner.iter_mut() {
*byte += b'0';
}
self.inner
}
pub const fn as_u64(&self) -> u64 {
let mut idx = L - 1;
let mut total: u64 = 0;
let mut base: u64 = 1;
while idx != 0 {
total += (self.inner[idx] as u64) * base;
idx -= 1;
base *= 10;
}
total += (self.inner[0] as u64) * base;
total
}
}
impl<const L: usize> AsRef<[u8; L]> for Digits<L> {
#[inline]
fn as_ref(&self) -> &[u8; L] {
&self.inner
}
}
impl<const L: usize> Index<usize> for Digits<L> {
type Output = u8;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
self.get(index).unwrap()
}
}
impl<const L: usize> Index<Range<usize>> for Digits<L> {
type Output = [u8];
#[inline]
fn index(&self, index: Range<usize>) -> &Self::Output {
self.inner.get(index).unwrap()
}
}
impl<const L: usize> AsRef<[u8]> for Digits<L> {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.inner
}
}
impl<const L: usize> core::fmt::Display for Digits<L> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let ascii_bytes = self.as_ascii_bytes();
f.write_str(unsafe { core::str::from_utf8_unchecked(&ascii_bytes) })
}
}
impl<const L: usize> core::str::FromStr for Digits<L> {
type Err = DigitsParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut digits = Digits { inner: [0; L] };
if s.len() != L {
return Err(DigitsParseError);
}
for (idx, byte) in s.as_bytes().iter().enumerate() {
if byte.is_ascii_digit() {
digits.inner[idx] = *byte - b'0';
} else {
return Err(DigitsParseError);
}
}
Ok(digits)
}
}
macro_rules! impl_as_number_fns {
($len:literal, safe_bounds = [$next:tt $($token:tt)*]) => {
impl Digits<$len> {
impl_as_number_fns!(@internal $len [$next $($token)*] );
}
};
(@internal $len:literal [u8 $($token:tt)*]) => {
#[inline]
pub const fn as_u8(&self) -> u8 {
self.as_u64() as u8
}
impl_as_number_fns!(@internal $len [$($token)*]);
};
(@internal $len:literal [u16 $($token:tt)*]) => {
#[inline]
pub const fn as_u16(&self) -> u16 {
self.as_u64() as u16
}
impl_as_number_fns!(@internal $len [$($token)*]);
};
(@internal $len:literal [u32 $($token:tt)*]) => {
#[inline]
pub const fn as_u32(&self) -> u32 {
self.as_u64() as u32
}
impl_as_number_fns!(@internal $len [$($token)*]);
};
(@internal $len:literal []) => { };
}
impl_as_number_fns!(9, safe_bounds = [u32]);
impl_as_number_fns!(8, safe_bounds = [u32]);
impl_as_number_fns!(7, safe_bounds = [u32]);
impl_as_number_fns!(6, safe_bounds = [u32]);
impl_as_number_fns!(5, safe_bounds = [u32]);
impl_as_number_fns!(4, safe_bounds = [u16 u32]);
impl_as_number_fns!(3, safe_bounds = [u16 u32]);
impl_as_number_fns!(2, safe_bounds = [u8 u16 u32]);
impl_as_number_fns!(1, safe_bounds = [u8 u16 u32]);