1#![allow(clippy::eval_order_dependence)] mod attributes;
4mod children;
5mod component;
6mod element;
7mod text;
8
9use proc_macro::TokenStream;
10use quote::{quote, ToTokens};
11use syn::ext::IdentExt;
12use syn::parse::{Parse, ParseStream};
13use syn::token::Paren;
14use syn::{parse_macro_input, Ident, LitStr, Result, Token};
15
16pub(crate) enum HtmlType {
17 Component,
18 Element,
19 Text,
20}
21
22pub(crate) enum HtmlTree {
23 Component(component::Component),
24 Element(element::Element),
25 Text(text::Text),
26}
27
28impl HtmlTree {
29 fn peek_type(input: ParseStream) -> Option<HtmlType> {
30 let input = input.fork(); if input.peek(LitStr) || input.peek(Paren) {
33 Some(HtmlType::Text)
34 } else if input.peek(Token![::]) {
35 Some(HtmlType::Component)
36 } else if input.peek(Ident::peek_any) {
37 let ident: Ident = input.call(Ident::parse_any).ok()?;
38 let ident = ident.to_string();
39
40 if ident.chars().next().unwrap().is_ascii_uppercase() || input.peek(Token![::]) {
41 Some(HtmlType::Component)
42 } else {
43 Some(HtmlType::Element)
44 }
45 } else {
46 None
47 }
48 }
49}
50
51impl Parse for HtmlTree {
52 fn parse(input: ParseStream) -> Result<Self> {
53 let html_type = match Self::peek_type(input) {
54 Some(html_type) => html_type,
55 None => return Err(input.error("expected a valid HTML node")),
56 };
57
58 Ok(match html_type {
59 HtmlType::Component => Self::Component(input.parse()?),
60 HtmlType::Element => Self::Element(input.parse()?),
61 HtmlType::Text => Self::Text(input.parse()?),
62 })
63 }
64}
65
66impl ToTokens for HtmlTree {
67 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
68 match self {
69 Self::Component(component) => component.to_tokens(tokens),
70 Self::Element(element) => element.to_tokens(tokens),
71 Self::Text(text) => text.to_tokens(tokens),
72 }
73 }
74}
75
76#[proc_macro]
80pub fn template(input: TokenStream) -> TokenStream {
81 let input = parse_macro_input!(input as HtmlTree);
82
83 let quoted = quote! {
84 ::maple_core::TemplateResult::new(::std::convert::Into::<_>::into(#input))
85 };
86
87 TokenStream::from(quoted)
88}