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