inpt 0.1.5

A derive crate for dumb type-level text parsing.
Documentation
use crate::{CharClass, Inpt, RecursionGuard};
use crate::{InptError, ResultExt};
use inpt_macros::impl_inpt_as;
use std::collections::{BTreeSet, HashSet};
use std::fmt;
use std::rc::Rc;
use std::sync::Arc;
use std::{cmp::Eq, hash::Hash, marker::PhantomData, str::FromStr};

impl<'s> Inpt<'s> for () {
    fn step(
        text: &'s str,
        end: bool,
        _trimmed: CharClass,
        _guard: &mut RecursionGuard,
    ) -> crate::InptStep<'s, Self> {
        crate::InptStep {
            data: if end && !text.is_empty() {
                Err(InptError::expected::<Self>(text))
            } else {
                Ok(())
            },
            rest: text,
        }
    }

    fn default_trim(_inherited: CharClass) -> CharClass {
        CharClass(&[])
    }
}

impl<'s> Inpt<'s> for &'s str {
    fn step(
        text: &'s str,
        _end: bool,
        _trimmed: CharClass,
        _guard: &mut RecursionGuard,
    ) -> crate::InptStep<'s, Self> {
        crate::InptStep {
            data: Ok(text),
            rest: "",
        }
    }
}

impl<'s> Inpt<'s> for char {
    fn step(
        text: &'s str,
        end: bool,
        _trimmed: CharClass,
        _guard: &mut RecursionGuard,
    ) -> crate::InptStep<'s, Self> {
        let c = text.chars().next();
        let rest = &text[c.map(|c| c.len_utf8()).unwrap_or(0)..];
        crate::InptStep {
            data: if end && !rest.is_empty() {
                Err(InptError::expected::<char>(text))
            } else {
                c.ok_or(InptError::expected_at_start::<Self>())
            },
            rest,
        }
    }
}

impl<'s> Inpt<'s> for String {
    fn step(
        text: &'s str,
        _end: bool,
        _trimmed: CharClass,
        _guard: &mut RecursionGuard,
    ) -> crate::InptStep<'s, Self> {
        crate::InptStep {
            data: Ok(text.to_string()),
            rest: "",
        }
    }
}

impl<'s, T: Inpt<'s>> Inpt<'s> for crate::error::InptResult<'s, T> {
    fn step(
        text: &'s str,
        end: bool,
        trimmed: CharClass,
        guard: &mut RecursionGuard,
    ) -> crate::InptStep<'s, Self> {
        guard.check(text, |guard| {
            let step = T::step(text, end, trimmed, guard);
            crate::InptStep {
                data: Ok(step.data),
                rest: step.rest,
            }
        })
    }

    fn default_trim(inherited: CharClass) -> CharClass {
        T::default_trim(inherited)
    }
}

impl_inpt_as!(
    impl<'s, T: Inpt<'s>> Box<T> as T => |data| Box::new(data),
    impl<'s, T: Inpt<'s>> Rc<T>  as T => |data| Rc::new(data),
    impl<'s, T: Inpt<'s>> Arc<T> as T => |data| Arc::new(data),
);

#[derive(Inpt)]
#[inpt(regex = r"(-?\d+(?:\.\d+)?(?:e-?\d+)?)", trim = r"\s,;")]
pub struct Num<T: FromStr>
where
    T::Err: fmt::Display,
{
    #[inpt(from_str)]
    pub inner: T,
}

impl_inpt_as!(
    impl f32 as Num<f32> => |num| num.inner,
    impl f64 as Num<f64> => |num| num.inner,
    impl u8 as Num<u8>   => |num| num.inner,
    impl i8 as Num<i8>   => |num| num.inner,
    impl u16 as Num<u16> => |num| num.inner,
    impl i16 as Num<i16> => |num| num.inner,
    impl u32 as Num<u32> => |num| num.inner,
    impl i32 as Num<i32> => |num| num.inner,
    impl u64 as Num<u64> => |num| num.inner,
    impl i64 as Num<i64> => |num| num.inner,
    impl usize as Num<usize> => |num| num.inner,
    impl isize as Num<isize> => |num| num.inner,
);

#[cfg(feature = "num-bigint")]
impl_inpt_as!(
    impl num_bigint::BigInt as Num<num_bigint::BigInt> => |num| num.inner,
    impl num_bigint::BigUint as Num<num_bigint::BigUint> => |num| num.inner,
);

#[derive(Inpt)]
pub struct Sequence<'s, I, T>
where
    T: Inpt<'s>,
    I: FromIterator<T>,
{
    #[inpt(from_iter = "T")]
    pub inner: I,
    #[inpt(skip)]
    item: PhantomData<&'s T>,
}

impl_inpt_as!(
    impl<'s, T: Inpt<'s>>             Vec<T>      as Sequence<'s, Self, T> => |seq| seq.inner,
    impl<'s, T: Inpt<'s> + Ord>       BTreeSet<T> as Sequence<'s, Self, T> => |seq| seq.inner,
    impl<'s, T: Inpt<'s> + Hash + Eq> HashSet<T>  as Sequence<'s, Self, T> => |seq| seq.inner,
);

impl<'s, T: Inpt<'s>, const N: usize> Inpt<'s> for [T; N] {
    fn step(
        text: &'s str,
        end: bool,
        trimmed: CharClass,
        guard: &mut RecursionGuard,
    ) -> crate::InptStep<'s, Self> {
        guard.check(text, |guard| {
            let mut data = [(); N].map(|()| Option::<T>::None);
            let mut rest = text;
            let trim_each = T::default_trim(trimmed);
            for i in 0..N {
                rest = trim_each.trim(rest, end && i + 1 == N);
                let step = T::step(rest, end && i + 1 == N, trim_each, guard);
                rest = step.rest;
                data[i] = match step.data {
                    Ok(seq) => Some(seq),
                    Err(err) => {
                        return crate::InptStep {
                            data: Err(err).within(text).expected::<Self>(),
                            rest,
                        }
                    }
                };
            }
            crate::InptStep {
                data: Ok(data.map(|opt| opt.unwrap())),
                rest,
            }
        })
    }

    fn default_trim(inherited: CharClass) -> CharClass {
        T::default_trim(inherited)
    }
}

inpt_macros::generate_inpt_impls!();