Skip to main content

zyn_core/ast/at/
match_node.rs

1use proc_macro2::Ident;
2use proc_macro2::Span;
3use proc_macro2::TokenStream;
4use proc_macro2::TokenTree;
5
6use quote::ToTokens;
7use quote::quote;
8
9use syn::Token;
10use syn::parse::Parse;
11use syn::parse::ParseStream;
12
13use crate::template::Template;
14
15use crate::Expand;
16
17/// A `@match` expression.
18///
19/// ```text
20/// @match (expr) {
21///     Some(v) => { {{ v }} },
22///     None    => { default },
23/// }
24/// ```
25pub struct MatchNode {
26    /// Source span of the `@` token.
27    pub span: Span,
28    /// The expression to match on.
29    pub expr: TokenStream,
30    /// Match arms as `(pattern, body)` pairs.
31    pub arms: Vec<(TokenStream, Template)>,
32}
33
34impl MatchNode {
35    pub fn span(&self) -> Span {
36        self.span
37    }
38}
39
40impl Parse for MatchNode {
41    fn parse(input: ParseStream) -> syn::Result<Self> {
42        let expr_content;
43        syn::parenthesized!(expr_content in input);
44        let expr: TokenStream = expr_content.parse()?;
45
46        let arms_content;
47        syn::braced!(arms_content in input);
48
49        let mut arms = Vec::new();
50
51        while !arms_content.is_empty() {
52            let mut pattern = TokenStream::new();
53
54            while !arms_content.is_empty() {
55                if arms_content.peek(Token![=>]) {
56                    arms_content.parse::<Token![=>]>()?;
57                    break;
58                }
59
60                let tt: TokenTree = arms_content.parse()?;
61                tt.to_tokens(&mut pattern);
62            }
63
64            let body_content;
65            syn::braced!(body_content in arms_content);
66            let body = body_content.parse::<Template>()?;
67
68            arms.push((pattern, body));
69
70            if arms_content.peek(Token![,]) {
71                arms_content.parse::<Token![,]>()?;
72            }
73        }
74
75        Ok(Self {
76            span: Span::call_site(),
77            expr,
78            arms,
79        })
80    }
81}
82
83impl Expand for MatchNode {
84    fn expand(&self, output: &Ident, idents: &mut crate::ident::Iter) -> TokenStream {
85        let expr = &self.expr;
86        let arms: Vec<TokenStream> = self
87            .arms
88            .iter()
89            .map(|(pattern, body)| {
90                let body_expanded = body.expand(output, idents);
91                quote! { #pattern => { #body_expanded } }
92            })
93            .collect();
94
95        quote! {
96            match #expr {
97                #(#arms),*
98            }
99        }
100    }
101}