etest-impl 0.3.0

Implementation of etest's proc_macro part
Documentation
use proc_macro::{TokenStream, TokenTree};

use crate::utils::err;

use super::ConfigItem;

pub struct ConfigIterator(<TokenStream as IntoIterator>::IntoIter);

impl ConfigIterator {
    pub fn new(t: TokenStream) -> Self {
        t.into()
    }
}

impl From<TokenStream> for ConfigIterator {
    fn from(val: TokenStream) -> Self {
        ConfigIterator(val.into_iter())
    }
}

impl Iterator for ConfigIterator {
    type Item = Result<ConfigItem, TokenStream>;

    fn next(&mut self) -> Option<Self::Item> {
        let key = match self.0.next() {
            None			=> return None,
            Some(TokenTree::Ident(i))	=> i,
            Some(t)			=> return Some(Err(err(t.span(), "unexpected key"))),
        };

        let val = match self.0.next() {
            Some(TokenTree::Punct(p)) if p.as_char() == ','	=> None,
            Some(TokenTree::Punct(p)) if p.as_char() == '='	=>
                Some(self.0
                     .by_ref()
                     .take_while(|t| !matches!(t, TokenTree::Punct(p) if p.as_char() == ','))
                     .collect()),
            Some(t)			=> return Some(Err(err(t.span(), "unexpected delimeter"))),
            None			=> None,
        };

        Some(Ok(ConfigItem {
            key:	key,
            val:	val,
        }))
    }
}

pub struct ListIterator(<TokenStream as IntoIterator>::IntoIter);

impl ListIterator {
    pub fn new(t: TokenStream) -> Self {
        t.into()
    }
}

impl From<TokenStream> for ListIterator {
    fn from(val: TokenStream) -> Self {
        ListIterator(val.into_iter())
    }
}

impl Iterator for ListIterator {
    type Item = Result<TokenStream, TokenStream>;

    fn next(&mut self) -> Option<Self::Item> {
        let mut res = Vec::new();
        let mut is_first = true;
        let mut need_elem = true;

        loop {
            match self.0.next() {
                None if is_first	=> return None,
                None			=> break,

                Some(TokenTree::Punct(p))
                    if p.as_char() == ',' && !need_elem		=> break,

                Some(TokenTree::Punct(p)) if p.as_char() == ','	=>
                    return Some(Err(err(p.span(), "unexpected delimeter"))),

                Some(tt)	=> {
                    res.push(tt);
                    need_elem = false;
                }
            }

            is_first = false;
        }

        Some(Ok(res.into_iter().collect()))
    }
}