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 {
28 pub span: Span,
30 pub branches: Vec<(TokenStream, Template)>,
33 pub else_body: Option<Box<Template>>,
35}
36
37impl IfNode {
38 pub fn span(&self) -> Span {
39 self.span
40 }
41}
42
43impl Parse for IfNode {
44 fn parse(input: ParseStream) -> syn::Result<Self> {
45 let mut branches = Vec::new();
46 let mut else_body = None;
47
48 let cond_content;
49 syn::parenthesized!(cond_content in input);
50 let condition: TokenStream = cond_content.parse()?;
51
52 let body_content;
53 syn::braced!(body_content in input);
54 let body = body_content.parse::<Template>()?;
55
56 branches.push((condition, body));
57
58 while is_at_else(input) {
59 input.parse::<Token![@]>()?;
60 input.call(syn::Ident::parse_any)?;
61
62 if is_keyword_if(input) {
63 input.call(syn::Ident::parse_any)?;
64
65 let cond_content;
66 syn::parenthesized!(cond_content in input);
67 let condition: TokenStream = cond_content.parse()?;
68
69 let body_content;
70 syn::braced!(body_content in input);
71 let body = body_content.parse::<Template>()?;
72
73 branches.push((condition, body));
74 } else {
75 let body_content;
76 syn::braced!(body_content in input);
77 else_body = Some(Box::new(body_content.parse::<Template>()?));
78 break;
79 }
80 }
81
82 Ok(Self {
83 span: Span::call_site(),
84 branches,
85 else_body,
86 })
87 }
88}
89
90impl Expand for IfNode {
91 fn expand(&self, output: &Ident, idents: &mut crate::ident::Iter) -> TokenStream {
92 let mut result = TokenStream::new();
93
94 for (i, (cond, body)) in self.branches.iter().enumerate() {
95 let body_expanded = body.expand(output, idents);
96
97 if i == 0 {
98 result = quote! { if #cond { #body_expanded } };
99 } else {
100 result = quote! { #result else if #cond { #body_expanded } };
101 }
102 }
103
104 if let Some(else_body) = &self.else_body {
105 let else_expanded = else_body.expand(output, idents);
106 result = quote! { #result else { #else_expanded } };
107 }
108
109 result
110 }
111}
112
113fn is_at_else(input: ParseStream) -> bool {
114 if !input.peek(Token![@]) {
115 return false;
116 }
117
118 let fork = input.fork();
119 let _ = fork.parse::<Token![@]>();
120
121 match fork.call(syn::Ident::parse_any) {
122 Ok(ident) => ident == "else",
123 Err(_) => false,
124 }
125}
126
127fn is_keyword_if(input: ParseStream) -> bool {
128 let fork = input.fork();
129
130 match fork.call(syn::Ident::parse_any) {
131 Ok(ident) => ident == "if",
132 Err(_) => false,
133 }
134}