templr_parser 0.2.1

Parser for templr templates
Documentation
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{
    ext::*,
    parse::{Parse, ParseStream},
    token::Brace,
    Ident, LitFloat, LitInt, LitStr, Token,
};

use crate::{Block, If, Let, Match, Name, Scope};

#[derive(Debug, Clone)]
pub enum HtmlAttrValue {
    Ident(Token![=], Ident),
    Float(Token![=], LitFloat),
    Int(Token![=], LitInt),
    Str(Token![=], LitStr),
    Block(Option<Token![?]>, Token![=], Block),
    None,
}

impl Parse for HtmlAttrValue {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        if input.peek(Token![=]) {
            let eq = input.parse()?;
            let lookahead1 = input.lookahead1();
            if lookahead1.peek(LitStr) {
                Ok(Self::Str(eq, input.parse()?))
            } else if lookahead1.peek(LitInt) {
                Ok(Self::Int(eq, input.parse()?))
            } else if lookahead1.peek(LitFloat) {
                Ok(Self::Float(eq, input.parse()?))
            } else if lookahead1.peek(Ident::peek_any) {
                Ok(Self::Ident(eq, Ident::parse_any(input)?))
            } else if lookahead1.peek(Brace) {
                Ok(Self::Block(None, eq, input.parse()?))
            } else {
                Err(lookahead1.error())
            }
        } else if input.peek(Token![?]) {
            Ok(Self::Block(
                Some(input.parse()?),
                input.parse()?,
                input.parse()?,
            ))
        } else {
            Ok(Self::None)
        }
    }
}

impl ToTokens for HtmlAttrValue {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            Self::Ident(eq, slf) => {
                eq.to_tokens(tokens);
                slf.to_tokens(tokens);
            }
            Self::Str(eq, slf) => {
                eq.to_tokens(tokens);
                slf.to_tokens(tokens);
            }
            Self::Int(eq, slf) => {
                eq.to_tokens(tokens);
                slf.to_tokens(tokens);
            }
            Self::Float(eq, slf) => {
                eq.to_tokens(tokens);
                slf.to_tokens(tokens);
            }
            Self::Block(eq, toggle, slf) => {
                eq.to_tokens(tokens);
                toggle.to_tokens(tokens);
                slf.to_tokens(tokens);
            }
            Self::None => {}
        }
    }
}

#[derive(Debug, Clone)]
pub struct HtmlAttr {
    pub name: Name,
    pub value: HtmlAttrValue,
}

impl Parse for HtmlAttr {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(Self {
            name: Name::parse_attr(input)?,
            value: input.parse()?,
        })
    }
}

impl ToTokens for HtmlAttr {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        self.name.to_tokens(tokens);
        self.value.to_tokens(tokens);
    }
}

#[derive(Debug, Clone)]
pub enum Attr {
    Html(HtmlAttr),
    If(If<Attr>),
    Match(Match<Attr>),
    Scope(Scope<Attr>),
    Let(Let),
    Spread(Block),
}

impl Parse for Attr {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        if input.peek(Token![#]) {
            let fork = input.fork();
            let _: Token![#] = fork.parse()?;

            let lookahead1 = fork.lookahead1();
            if lookahead1.peek(Token![if]) {
                Ok(Self::If(input.parse()?))
            } else if lookahead1.peek(Token![match]) {
                Ok(Self::Match(input.parse()?))
            } else if lookahead1.peek(Brace) {
                Ok(Self::Scope(input.parse()?))
            } else if lookahead1.peek(Token![let]) {
                Ok(Self::Let(input.parse()?))
            } else {
                Err(lookahead1.error())
            }
        } else if input.peek(Brace) {
            Ok(Self::Spread(input.parse()?))
        } else {
            Ok(Self::Html(input.parse()?))
        }
    }
}

impl ToTokens for Attr {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            Self::Html(slf) => slf.to_tokens(tokens),
            Self::If(slf) => slf.to_tokens(tokens),
            Self::Match(slf) => slf.to_tokens(tokens),
            Self::Scope(slf) => slf.to_tokens(tokens),
            Self::Let(slf) => slf.to_tokens(tokens),
            Self::Spread(slf) => slf.to_tokens(tokens),
        }
    }
}