zyn_core/ast/at/
if_node.rs1use 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}