Skip to main content

zyn_core/ast/at/
if_node.rs

1use proc_macro2::Ident;
2use proc_macro2::Span;
3use proc_macro2::TokenStream;
4
5use quote::quote;
6
7use syn::Token;
8use syn::ext::IdentExt;
9use syn::parse::Parse;
10use syn::parse::ParseStream;
11
12use crate::template::Template;
13
14use crate::Expand;
15
16pub struct IfNode {
17    pub span: Span,
18    pub branches: Vec<(TokenStream, Template)>,
19    pub else_body: Option<Box<Template>>,
20}
21
22impl IfNode {
23    pub fn span(&self) -> Span {
24        self.span
25    }
26}
27
28impl Parse for IfNode {
29    fn parse(input: ParseStream) -> syn::Result<Self> {
30        let mut branches = Vec::new();
31        let mut else_body = None;
32
33        let cond_content;
34        syn::parenthesized!(cond_content in input);
35        let condition: TokenStream = cond_content.parse()?;
36
37        let body_content;
38        syn::braced!(body_content in input);
39        let body = body_content.parse::<Template>()?;
40
41        branches.push((condition, body));
42
43        while is_at_else(input) {
44            input.parse::<Token![@]>()?;
45            input.call(syn::Ident::parse_any)?;
46
47            if is_keyword_if(input) {
48                input.call(syn::Ident::parse_any)?;
49
50                let cond_content;
51                syn::parenthesized!(cond_content in input);
52                let condition: TokenStream = cond_content.parse()?;
53
54                let body_content;
55                syn::braced!(body_content in input);
56                let body = body_content.parse::<Template>()?;
57
58                branches.push((condition, body));
59            } else {
60                let body_content;
61                syn::braced!(body_content in input);
62                else_body = Some(Box::new(body_content.parse::<Template>()?));
63                break;
64            }
65        }
66
67        Ok(Self {
68            span: Span::call_site(),
69            branches,
70            else_body,
71        })
72    }
73}
74
75impl Expand for IfNode {
76    fn expand(&self, output: &Ident, idents: &mut crate::ident::Iter) -> TokenStream {
77        let mut result = TokenStream::new();
78
79        for (i, (cond, body)) in self.branches.iter().enumerate() {
80            let body_expanded = body.expand(output, idents);
81
82            if i == 0 {
83                result = quote! { if #cond { #body_expanded } };
84            } else {
85                result = quote! { #result else if #cond { #body_expanded } };
86            }
87        }
88
89        if let Some(else_body) = &self.else_body {
90            let else_expanded = else_body.expand(output, idents);
91            result = quote! { #result else { #else_expanded } };
92        }
93
94        result
95    }
96}
97
98fn is_at_else(input: ParseStream) -> bool {
99    if !input.peek(Token![@]) {
100        return false;
101    }
102
103    let fork = input.fork();
104    let _ = fork.parse::<Token![@]>();
105
106    match fork.call(syn::Ident::parse_any) {
107        Ok(ident) => ident == "else",
108        Err(_) => false,
109    }
110}
111
112fn is_keyword_if(input: ParseStream) -> bool {
113    let fork = input.fork();
114
115    match fork.call(syn::Ident::parse_any) {
116        Ok(ident) => ident == "if",
117        Err(_) => false,
118    }
119}