anyseq 0.2.0

Automatically generate serde Deserializer from separated values.
Documentation
use winnow::{
    ascii::{line_ending, space0},
    combinator::{alt, eof, preceded},
    stream::{Compare, Stream, StreamIsPartial},
    token::take_while,
    PResult, Parser,
};

use crate::de::{self, Deserializer};
use crate::{Lexer, Token};

pub struct Spaces;

impl<I, O> Lexer<I, Token<O>> for Spaces
where
    I: StreamIsPartial + Stream<Token = char, Slice = O> + Compare<char> + Compare<&'static str>,
{
    type Error = winnow::error::ErrMode<winnow::error::ContextError>;

    fn item(input: &mut I) -> Result<Token<I::Slice>, Self::Error> {
        alt((
            eof.map(|_| Token::SeqEnd),
            line_ending.map(|_| Token::SeqEnd),
            preceded(
                space0,
                take_while(0.., |c| c != ' ' && c != '\t' && c != '\n' && c != '\r'),
            )
            .map(Token::Item),
        ))
        .parse_next(input)
    }
}

pub fn from_str<'a, T>(
    s: &'a str,
) -> Result<T, de::Error<<Spaces as Lexer<&str, Token<&str>>>::Error>>
where
    T: serde::de::Deserialize<'a>,
{
    use serde::de::Error;

    let mut deserializer = Deserializer::<Spaces, _, _>::new(s);
    let t = T::deserialize(&mut deserializer)?;
    if deserializer.input.is_empty() {
        Ok(t)
    } else {
        Err(de::Error::custom(format!(
            "input not empty: {:?}",
            deserializer.input
        )))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_space0() {
        assert_eq!(vec![1, 2, 3], from_str::<Vec<i8>>("1 2 3").unwrap());

        #[derive(Debug, PartialEq, serde::Deserialize)]
        struct Struct<'a> {
            a: i8,
            b: Option<&'a str>,
            #[serde(default)]
            c: Option<u32>,
        }
        assert_eq!(
            Struct {
                a: 1,
                b: Some("foo"),
                c: None
            },
            from_str::<Struct>("1 foo").unwrap()
        );
        assert_eq!(
            Struct {
                a: 1,
                b: Some("foo"),
                c: Some(5)
            },
            from_str::<Struct>("1 foo 5").unwrap()
        );
    }
}