markup-proc-macro 0.16.0

A blazing fast, type-safe template engine for Rust.
Documentation
use crate::ast::{
    Attribute, Element, For, If, IfClause, IfClauseTest, Match, MatchClause, Node, Struct, Template,
};
use syn::parse::{Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{LitBool, LitStr};

impl Parse for Struct {
    fn parse(input: ParseStream) -> Result<Self> {
        let start_input_len = input.to_string().len();
        let attributes = input.call(syn::Attribute::parse_outer)?;
        let name = input.parse()?;
        let generics = input.parse()?;
        let fields = {
            if input.peek(syn::token::Paren) {
                let fields;
                syn::parenthesized!(fields in input);
                Punctuated::<syn::Field, syn::token::Comma>::parse_terminated_with(
                    &fields,
                    syn::Field::parse_named,
                )?
                .into_pairs()
                .map(|pair| pair.into_value())
                .collect()
            } else {
                Vec::new()
            }
        };
        let where_clause = if input.peek(syn::token::Where) {
            Some(input.parse()?)
        } else {
            None
        };
        let mut children = Vec::new();
        let inner;
        syn::braced!(inner in input);
        while !inner.is_empty() {
            children.push(inner.parse()?);
        }
        // We use the length of the tokens that define this template as a rough estimate of the
        // number of bytes the output of this template will occupy.
        // Lifted from Maud [1].
        // [1]: https://github.com/lfairy/maud/blob/13a5cfcaa31b3f6e2deb015ea49ef87d285cef7c/maud_macros/src/lib.rs#L38-L40
        let size_hint = start_input_len - input.to_string().len();
        Ok(Struct {
            name,
            attributes,
            generics,
            where_clause,
            fields,
            children,
            size_hint,
        })
    }
}

impl Parse for Template {
    fn parse(input: ParseStream) -> Result<Self> {
        let children = input.parse::<Many<Node>>()?.0;
        Ok(Self { children })
    }
}

impl Parse for Node {
    fn parse(input: ParseStream) -> Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(syn::Ident)
            || lookahead.peek(syn::Token![#])
            || lookahead.peek(syn::Token![.])
            || lookahead.peek(syn::Token![$])
        {
            Ok(Node::Element(input.parse()?))
        } else if lookahead.peek(syn::Token![@]) {
            let _: syn::Token![@] = input.parse()?;
            let lookahead = input.lookahead1();
            if lookahead.peek(syn::token::If) {
                let _: syn::token::If = input.parse()?;
                Ok(Node::If(input.parse()?))
            } else if lookahead.peek(syn::token::For) {
                let _: syn::token::For = input.parse()?;
                Ok(Node::For(input.parse()?))
            } else if lookahead.peek(syn::token::Match) {
                let _: syn::token::Match = input.parse()?;
                Ok(Node::Match(input.parse()?))
            } else if lookahead.peek(syn::Lit)
                || lookahead.peek(syn::Ident)
                || lookahead.peek(syn::token::Brace)
                || lookahead.peek(syn::token::Crate)
                || lookahead.peek(syn::token::SelfValue)
                || lookahead.peek(syn::token::SelfType)
                || lookahead.peek(syn::token::Super)
            {
                Ok(Node::Expr(input.parse()?))
            } else if input.fork().parse::<syn::Stmt>().is_ok() {
                Ok(Node::Stmt(input.parse()?))
            } else {
                Err(lookahead.error())
            }
        } else if lookahead.peek(syn::Lit) {
            let lit: syn::Lit = input.parse()?;
            Ok(Node::Expr(syn::parse_quote_spanned!(lit.span() => #lit)))
        } else if lookahead.peek(syn::token::Brace) {
            Ok(Node::Expr(input.parse()?))
        } else {
            Err(lookahead.error())
        }
    }
}

impl Parse for Element {
    fn parse(input: ParseStream) -> Result<Self> {
        let (name, mut id, mut classes) = {
            let lookahead = input.lookahead1();
            if lookahead.peek(syn::Token![$]) {
                let _: syn::Token![$] = input.parse()?;
                let name = identifier_or_string_literal_or_expression(input)?;
                (name, None, Vec::new())
            } else if lookahead.peek(syn::Ident) {
                let name = identifier_or_string_literal_or_expression(input)?;
                (name, None, Vec::new())
            } else if lookahead.peek(syn::Token![#]) {
                let token: syn::Token![#] = input.parse()?;
                let div = LitStr::new("div", token.span);
                (
                    syn::parse_quote_spanned!(token.span => #div),
                    Some(identifier_or_string_literal_or_expression(input)?),
                    Vec::new(),
                )
            } else if lookahead.peek(syn::Token![.]) {
                let token: syn::Token![.] = input.parse()?;
                let div = LitStr::new("div", token.span);
                (
                    syn::parse_quote_spanned!(token.span => #div),
                    None,
                    vec![identifier_or_string_literal_or_expression(input)?],
                )
            } else {
                return Err(lookahead.error());
            }
        };

        loop {
            if input.peek(syn::Token![#]) {
                let _: syn::Token![#] = input.parse()?;
                id = Some(identifier_or_string_literal_or_expression(input)?);
            } else if input.peek(syn::Token![.]) {
                let _: syn::Token![.] = input.parse()?;
                classes.push(identifier_or_string_literal_or_expression(input)?);
            } else {
                break;
            }
        }

        let attributes = {
            if input.peek(syn::token::Bracket) {
                let attributes;
                syn::bracketed!(attributes in input);
                Punctuated::<Attribute, syn::Token![,]>::parse_terminated(&attributes)?
                    .into_pairs()
                    .map(|a| a.into_value())
                    .collect()
            } else {
                Vec::new()
            }
        };

        let (children, close) = {
            let lookahead = input.lookahead1();
            if lookahead.peek(syn::token::Semi) {
                let _: syn::Token![;] = input.parse()?;
                (Vec::new(), false)
            } else if lookahead.peek(syn::token::Brace) {
                let children;
                syn::braced!(children in input);
                (children.parse::<Many<_>>()?.0, true)
            } else if lookahead.peek(syn::LitStr) {
                let string = input.parse::<syn::LitStr>()?;
                (
                    vec![syn::parse_quote_spanned!(string.span() => #string)],
                    true,
                )
            } else {
                return Err(lookahead.error());
            }
        };

        Ok(Element {
            name,
            id,
            classes,
            attributes,
            children,
            close,
        })
    }
}

impl Parse for If {
    fn parse(input: ParseStream) -> Result<Self> {
        let mut clauses = vec![input.parse()?];
        let mut default = None;
        loop {
            if input.peek(syn::token::Else) {
                let _: syn::token::Else = input.parse()?;
                if input.peek(syn::token::If) {
                    let _: syn::token::If = input.parse()?;
                    clauses.push(input.parse()?);
                } else {
                    default = {
                        let default;
                        syn::braced!(default in input);
                        Some(default.parse::<Many<_>>()?.0)
                    };
                    break;
                }
            } else {
                break;
            }
        }
        Ok(If { clauses, default })
    }
}

impl Parse for IfClause {
    fn parse(input: ParseStream) -> Result<Self> {
        let test = input.parse()?;
        let consequent = {
            let consequent;
            syn::braced!(consequent in input);
            consequent.parse::<Many<_>>()?.0
        };
        Ok(IfClause { test, consequent })
    }
}

impl Parse for IfClauseTest {
    fn parse(input: ParseStream) -> Result<Self> {
        if input.peek(syn::token::Let) {
            let _: syn::token::Let = input.parse()?;
            let pattern = syn::Pat::parse_multi_with_leading_vert(input)?;
            let _: syn::Token![=] = input.parse()?;
            let expr = syn::Expr::parse_without_eager_brace(input)?;
            Ok(IfClauseTest::Let(pattern, expr))
        } else {
            Ok(IfClauseTest::Expr(syn::Expr::parse_without_eager_brace(
                input,
            )?))
        }
    }
}

impl Parse for Match {
    fn parse(input: ParseStream) -> Result<Self> {
        let expr = syn::Expr::parse_without_eager_brace(input)?;
        let inner;
        syn::braced!(inner in input);
        let clauses = inner.parse::<Many<_>>()?.0;
        Ok(Match { expr, clauses })
    }
}

impl Parse for MatchClause {
    fn parse(input: ParseStream) -> Result<Self> {
        let pat = syn::Pat::parse_multi_with_leading_vert(input)?;
        let guard = if input.peek(syn::Token![if]) {
            let _: syn::Token![if] = input.parse()?;
            Some(input.parse()?)
        } else {
            None
        };
        let _: syn::Token![=>] = input.parse()?;
        let inner;
        syn::braced!(inner in input);
        let consequent = inner.parse::<Many<_>>()?.0;
        Ok(MatchClause {
            pat,
            guard,
            consequent,
        })
    }
}

impl Parse for For {
    fn parse(input: ParseStream) -> Result<Self> {
        let pat = syn::Pat::parse_multi_with_leading_vert(input)?;
        let _: syn::token::In = input.parse()?;
        let expr = syn::Expr::parse_without_eager_brace(input)?;
        let body;
        syn::braced!(body in input);
        let body = body.parse::<Many<_>>()?.0;
        Ok(For { pat, expr, body })
    }
}

impl Parse for Attribute {
    fn parse(input: ParseStream) -> Result<Self> {
        if input.peek(syn::Token![..]) {
            let _: syn::Token![..] = input.parse()?;
            return Ok(Attribute::Many(input.parse()?));
        }

        let name = identifier_or_string_literal_or_expression(input)?;
        let value = if input.peek(syn::Token![=]) {
            let _: syn::Token![=] = input.parse()?;
            input.parse()?
        } else {
            let span = name.span();
            let lit = LitBool::new(true, span);
            syn::parse_quote_spanned!(span => #lit)
        };
        Ok(Attribute::One(name, value))
    }
}

#[derive(Debug)]
pub struct Many<P>(pub Vec<P>);

impl<P: Parse> Parse for Many<P> {
    fn parse(input: ParseStream) -> Result<Self> {
        let mut items = Vec::new();
        while !input.is_empty() {
            items.push(input.parse()?);
        }
        Ok(Many(items))
    }
}

fn identifier_or_string_literal_or_expression(input: ParseStream) -> Result<syn::Expr> {
    use syn::ext::IdentExt;
    let lookahead = input.lookahead1();
    if lookahead.peek(syn::Ident::peek_any) {
        let ident = syn::Ident::parse_any(input)?;
        let string = LitStr::new(&ident.unraw().to_string(), ident.span());
        Ok(syn::parse_quote_spanned!(ident.span() => #string))
    } else if lookahead.peek(syn::LitStr) {
        let lit_str = &input.parse::<syn::LitStr>()?;
        let string = LitStr::new(&lit_str.value(), lit_str.span());
        Ok(syn::parse_quote_spanned!(lit_str.span() => #string))
    } else if lookahead.peek(syn::token::Brace) {
        let inner;
        syn::braced!(inner in input);
        Ok(inner.parse()?)
    } else {
        Err(lookahead.error())
    }
}