#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
#![allow(clippy::result_unit_err)]
#[doc(hidden)]
pub extern crate strp_macros as macros;
extern crate self as strp;
#[cfg(test)]
mod tests;
#[doc(hidden)]
pub mod __private {
pub extern crate alloc;
pub use macros;
use crate::TryParseError;
use core::iter::Peekable;
pub struct Hex<T>(T);
pub struct Binary<T>(T);
impl<T> Hex<T> {
#[inline(always)]
pub fn new(val: T) -> Self {
Self(val)
}
#[inline(always)]
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> Binary<T> {
#[inline(always)]
pub fn new(val: T) -> Self {
Self(val)
}
#[inline(always)]
pub fn into_inner(self) -> T {
self.0
}
}
macro_rules! impl_hex__and_binary_for_primitives {
($($ty:ty),*) => {
$(
impl crate::TryParse for Hex<$ty> {
type Err = core::num::ParseIntError;
fn try_parse(
iter: &mut impl Iterator<Item = u8>,
) -> Result<Self, TryParseError<Self::Err>> {
let vec = iter.collect::<alloc::vec::Vec<u8>>();
let str = core::str::from_utf8(&vec)
.or(Err(TryParseError::InvalidUtf8String))?;
Ok(Self(<$ty>::from_str_radix(&str, 16)?))
}
}
impl From<Hex<$ty>> for $ty{
fn from(hex: Hex<$ty>) -> Self {
hex.0
}
}
impl crate::TryParse for Binary<$ty> {
type Err = core::num::ParseIntError;
fn try_parse(
iter: &mut impl Iterator<Item = u8>,
) -> Result<Self, TryParseError<Self::Err>> {
let vec = iter.collect::<alloc::vec::Vec<u8>>();
let str = core::str::from_utf8(&vec)
.or(Err(TryParseError::InvalidUtf8String))?;
Ok(Self(<$ty>::from_str_radix(&str, 2)?))
}
}
impl From<Binary<$ty>> for $ty{
fn from(bin: Binary<$ty>) -> Self {
bin.0
}
}
)*
};
}
impl_hex__and_binary_for_primitives!(
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize
);
#[inline(always)]
pub fn parse_single<S: ::strp::TryParse>(
iter: &mut Peekable<impl Iterator<Item = u8> + Clone>,
m_str: &'static str,
delim: Option<u8>,
) -> Result<S, TryParseError<S::Err>> {
let cmp = m_str.bytes();
let iter_err = iter.clone();
if iter.by_ref().take(cmp.len()).eq(cmp) {
if let Some(delim) = delim {
let iter = iter.by_ref();
let mut iter = core::iter::from_fn(|| iter.next_if(|e| *e != delim));
S::try_parse(&mut iter)
} else {
S::try_parse(iter)
}
} else {
let mut iter_err = iter_err.take(m_str.len()).peekable();
return if let Some(delim) = delim {
let iter_err = core::iter::from_fn(|| iter_err.next_if(|e| *e != delim));
Err(TryParseError::ExpectedMismatch(
m_str,
iter_err.map(|b| b as char).collect(),
))
} else {
Err(TryParseError::ExpectedMismatch(
m_str,
iter_err.map(|b| b as char).collect(),
))
};
}
}
pub trait ParseMultiple: Sized {
fn parse_multiple(
iter: &mut Peekable<impl Iterator<Item = u8> + Clone>,
sparse_data: &[(&'static str, Option<u8>)],
) -> Result<Self, TryParseError<()>>;
}
impl<T: strp::TryParse, const LEN: usize> ParseMultiple for [T; LEN] {
#[inline(always)]
fn parse_multiple(
iter: &mut Peekable<impl Iterator<Item = u8> + Clone>,
sparse_data: &[(&'static str, Option<u8>)],
) -> Result<Self, TryParseError<()>> {
assert!(LEN == sparse_data.len());
let mut array: [T; LEN] = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
for i in 0..LEN {
let cur = unsafe { sparse_data.get_unchecked(i) };
*unsafe { array.get_unchecked_mut(i) } = match parse_single::<T>(iter, cur.0, cur.1)
{
Ok(ok) => ok,
Err(strp::TryParseError::Err(_)) => return Err(TryParseError::Err(())),
Err(TryParseError::InvalidUtf8String) => {
return Err(TryParseError::InvalidUtf8String)
}
Err(TryParseError::ExpectedMismatch(l, r)) => {
return Err(TryParseError::ExpectedMismatch(l, r))
}
}
}
Ok(array)
}
}
macro_rules! impl_sparse_multiple_tuple {
($first:ident, $($rest:ident),+; $size:expr;) => {
impl<$first: ::strp::TryParse, $($rest: ::strp::TryParse),+> ParseMultiple for ($first, $($rest),+) {
#[inline(always)]
fn parse_multiple(
iter: &mut Peekable<impl Iterator<Item = u8> + Clone>,
sparse_data: &[(&'static str, Option<u8>)],
) -> Result<Self, TryParseError<()>> {
assert!($size == sparse_data.len());
Ok(
macros::rep!($size[match parse_single(iter, sparse_data[#].0, sparse_data[#].1){
Ok(ok) => ok,
Err(strp::TryParseError::Err(_)) => return Err(TryParseError::Err(())),
Err(TryParseError::InvalidUtf8String) => {
return Err(TryParseError::InvalidUtf8String)
}
Err(TryParseError::ExpectedMismatch(l, r)) => {
return Err(TryParseError::ExpectedMismatch(l, r))
}
}])
)
}
}
};
}
impl_sparse_multiple_tuple!(A,B,C,D,E,F,G,H,J,I,K,L,M,N,O,P; 16;);
impl_sparse_multiple_tuple!(A,B,C,D,E,F,G,H,J,I,K,L,M,N,O; 15;);
impl_sparse_multiple_tuple!(A,B,C,D,E,F,G,H,J,I,K,L,M,N; 14;);
impl_sparse_multiple_tuple!(A,B,C,D,E,F,G,H,J,I,K,L,M; 13;);
impl_sparse_multiple_tuple!(A,B,C,D,E,F,G,H,J,I,K,L; 12;);
impl_sparse_multiple_tuple!(A,B,C,D,E,F,G,H,J,I,K; 11;);
impl_sparse_multiple_tuple!(A,B,C,D,E,F,G,H,J,I; 10;);
impl_sparse_multiple_tuple!(A,B,C,D,E,F,G,H,J; 9;);
impl_sparse_multiple_tuple!(A,B,C,D,E,F,G,H; 8;);
impl_sparse_multiple_tuple!(A,B,C,D,E,F,G; 7;);
impl_sparse_multiple_tuple!(A,B,C,D,E,F; 6;);
impl_sparse_multiple_tuple!(A,B,C,D,E; 5;);
impl_sparse_multiple_tuple!(A,B,C,D; 4;);
impl_sparse_multiple_tuple!(A,B,C; 3;);
impl_sparse_multiple_tuple!(A,B; 2;);
}
pub use macros::{parse, scan, try_parse, try_scan};
pub trait TryParse
where
Self: Sized,
{
type Err;
fn try_parse(iter: &mut impl Iterator<Item = u8>) -> Result<Self, TryParseError<Self::Err>>;
}
macro_rules! impl_from_str_tys {
($($ty:ty),*) => {
$(
impl TryParse for $ty where Self: ::core::str::FromStr {
type Err = <Self as ::core::str::FromStr>::Err;
fn try_parse(
iter: &mut impl core::iter::Iterator<Item = u8>,
) -> Result<Self, TryParseError<Self::Err>> {
Ok(::core::str::FromStr::from_str(
core::str::from_utf8(&iter.collect::<__private::alloc::vec::Vec<u8>>()).or(Err(TryParseError::InvalidUtf8String))?,
)?)
}
}
)*
};
}
impl_from_str_tys!(
u8,
i8,
u16,
i16,
u32,
i32,
u64,
i64,
u128,
i128,
f32,
f64,
__private::alloc::string::String
);
#[derive(PartialEq)]
pub enum TryParseError<T> {
ExpectedMismatch(&'static str, __private::alloc::string::String),
InvalidUtf8String,
Err(T),
}
impl<T: core::fmt::Debug> core::fmt::Debug for TryParseError<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::ExpectedMismatch(s, i) => write!(f, "expected: \"{s}\" but got: \"{i}\""),
Self::InvalidUtf8String => write!(f, "invalid utf8 encoding in source string"),
Self::Err(arg0) => arg0.fmt(f),
}
}
}
impl<T> From<T> for TryParseError<T> {
fn from(value: T) -> Self {
Self::Err(value)
}
}