Skip to main content

zyn_core/ast/at/
for_node.rs

1use proc_macro2::Ident;
2use proc_macro2::Span;
3use proc_macro2::TokenStream;
4
5use quote::quote;
6
7use syn::ext::IdentExt;
8use syn::parse::Parse;
9use syn::parse::ParseStream;
10
11use crate::template::Template;
12
13use crate::Expand;
14
15/// A `@for` loop.
16///
17/// Iterator form:
18/// ```text
19/// @for (item in fields.iter()) { {{ item.ident }}, }
20/// ```
21///
22/// Classic repeat form (repeats N times with no binding):
23/// ```text
24/// @for (3) { x, }   // → x, x, x,
25/// ```
26pub struct ForNode {
27    /// Source span of the `@` token.
28    pub span: Span,
29    /// The loop binding identifier. `_` for the classic repeat form.
30    pub binding: syn::Ident,
31    /// The iterator expression, e.g. `fields.iter()` or `0..3`.
32    pub iter: TokenStream,
33    /// The loop body template.
34    pub body: Box<Template>,
35}
36
37impl ForNode {
38    pub fn span(&self) -> Span {
39        self.span
40    }
41}
42
43impl Parse for ForNode {
44    fn parse(input: ParseStream) -> syn::Result<Self> {
45        let params;
46        syn::parenthesized!(params in input);
47
48        let fork = params.fork();
49        let is_classic = if fork.call(syn::Ident::parse_any).is_ok() {
50            let Ok(kw) = fork.call(syn::Ident::parse_any) else {
51                return Self::parse_classic(&params, input);
52            };
53            kw != "in"
54        } else {
55            true
56        };
57
58        if is_classic {
59            return Self::parse_classic(&params, input);
60        }
61
62        let binding: syn::Ident = params.call(syn::Ident::parse_any)?;
63        let _in_kw: syn::Ident = params.call(syn::Ident::parse_any)?;
64        let iter: TokenStream = params.parse()?;
65
66        let body_content;
67        syn::braced!(body_content in input);
68        let body = body_content.parse::<Template>()?;
69
70        Ok(Self {
71            span: Span::call_site(),
72            binding,
73            iter,
74            body: Box::new(body),
75        })
76    }
77}
78
79impl ForNode {
80    fn parse_classic(params: ParseStream, input: ParseStream) -> syn::Result<Self> {
81        let count: TokenStream = params.parse()?;
82        let binding = syn::Ident::new("_", Span::call_site());
83        let iter: TokenStream = quote! { 0..#count };
84
85        let body_content;
86        syn::braced!(body_content in input);
87        let body = body_content.parse::<Template>()?;
88
89        Ok(Self {
90            span: Span::call_site(),
91            binding,
92            iter,
93            body: Box::new(body),
94        })
95    }
96}
97
98impl Expand for ForNode {
99    fn expand(&self, output: &Ident, idents: &mut crate::ident::Iter) -> TokenStream {
100        let binding = &self.binding;
101        let iter = &self.iter;
102        let body_expanded = self.body.expand(output, idents);
103
104        quote! {
105            for #binding in #iter {
106                #body_expanded
107            }
108        }
109    }
110}