Skip to main content

zyn_core/ast/at/
element_node.rs

1use proc_macro2::Ident;
2use proc_macro2::Span;
3use proc_macro2::TokenStream;
4use proc_macro2::TokenTree;
5
6use quote::ToTokens;
7use quote::quote;
8
9use syn::Token;
10use syn::parse::Parse;
11use syn::parse::ParseStream;
12
13use crate::template::Template;
14
15use crate::Expand;
16use crate::pascal;
17
18pub struct ElementNode {
19    pub span: Span,
20    pub name: TokenStream,
21    pub props: Vec<(syn::Ident, TokenStream)>,
22    pub children: Option<Box<Template>>,
23}
24
25impl ElementNode {
26    pub fn span(&self) -> Span {
27        self.span
28    }
29
30    pub fn parse_with_ident(input: ParseStream, first_ident: syn::Ident) -> syn::Result<Self> {
31        let span = first_ident.span();
32
33        let mut name = TokenStream::new();
34        first_ident.to_tokens(&mut name);
35
36        while input.peek(Token![::]) {
37            let colons: Token![::] = input.parse()?;
38            colons.to_tokens(&mut name);
39            let segment: syn::Ident = input.parse()?;
40            segment.to_tokens(&mut name);
41        }
42
43        parse_props_and_children(input, span, name)
44    }
45}
46
47impl Expand for ElementNode {
48    fn expand(&self, output: &Ident, idents: &mut crate::ident::Iter) -> TokenStream {
49        let name = pascal!(self.name => token_stream);
50        let prop_names: Vec<&syn::Ident> = self.props.iter().map(|(n, _)| n).collect();
51        let prop_values: Vec<&TokenStream> = self.props.iter().map(|(_, v)| v).collect();
52
53        if let Some(children) = &self.children {
54            let inner = idents.next().unwrap();
55            let children_expanded = children.expand(&inner, idents);
56
57            quote! {
58                {
59                    let mut #inner = ::zyn::proc_macro2::TokenStream::new();
60                    #children_expanded
61                    let __zyn_rendered = ::zyn::Render::render(&#name {
62                        #(#prop_names: #prop_values,)*
63                        children: #inner,
64                    }, &::zyn::Input::from(input.clone()));
65                    ::zyn::quote::ToTokens::to_tokens(&__zyn_rendered, &mut #output);
66                }
67            }
68        } else {
69            quote! {
70                {
71                    let __zyn_rendered = ::zyn::Render::render(&#name {
72                        #(#prop_names: #prop_values,)*
73                    }, &::zyn::Input::from(input.clone()));
74                    ::zyn::quote::ToTokens::to_tokens(&__zyn_rendered, &mut #output);
75                }
76            }
77        }
78    }
79}
80
81impl Parse for ElementNode {
82    fn parse(input: ParseStream) -> syn::Result<Self> {
83        let first_ident: syn::Ident = input.parse()?;
84        let span = first_ident.span();
85
86        let mut name = TokenStream::new();
87        first_ident.to_tokens(&mut name);
88
89        while input.peek(Token![::]) {
90            let colons: Token![::] = input.parse()?;
91            colons.to_tokens(&mut name);
92            let segment: syn::Ident = input.parse()?;
93            segment.to_tokens(&mut name);
94        }
95
96        parse_props_and_children(input, span, name)
97    }
98}
99
100fn parse_props_and_children(
101    input: ParseStream,
102    span: Span,
103    name: TokenStream,
104) -> syn::Result<ElementNode> {
105    let mut props = Vec::new();
106
107    if input.peek(syn::token::Paren) {
108        let props_content;
109        syn::parenthesized!(props_content in input);
110
111        while !props_content.is_empty() {
112            let prop_name: syn::Ident = props_content.parse()?;
113            props_content.parse::<Token![=]>()?;
114
115            let mut value = TokenStream::new();
116
117            while !props_content.is_empty() && !props_content.peek(Token![,]) {
118                let tt: TokenTree = props_content.parse()?;
119                tt.to_tokens(&mut value);
120            }
121
122            props.push((prop_name, value));
123
124            if props_content.peek(Token![,]) {
125                props_content.parse::<Token![,]>()?;
126            }
127        }
128    }
129
130    let children = if input.peek(syn::token::Brace) {
131        let children_content;
132        syn::braced!(children_content in input);
133        Some(Box::new(children_content.parse::<Template>()?))
134    } else {
135        None
136    };
137
138    Ok(ElementNode {
139        span,
140        name,
141        props,
142        children,
143    })
144}