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    pub fn to_display_stream(&self, injections: &[(String, TokenStream)]) -> TokenStream {
40        let expr = &self.expr;
41        let arms: Vec<TokenStream> = self
42            .arms
43            .iter()
44            .map(|(pattern, body)| {
45                let body_display = body.to_display_stream(injections);
46                quote! { #pattern => { #body_display } }
47            })
48            .collect();
49
50        quote! { match #expr { #(#arms),* } }
51    }
52}
53
54impl Parse for MatchNode {
55    fn parse(input: ParseStream) -> syn::Result<Self> {
56        let expr_content;
57        syn::parenthesized!(expr_content in input);
58        let expr: TokenStream = expr_content.parse()?;
59
60        let arms_content;
61        syn::braced!(arms_content in input);
62
63        let mut arms = Vec::new();
64
65        while !arms_content.is_empty() {
66            let mut pattern = TokenStream::new();
67
68            while !arms_content.is_empty() {
69                if arms_content.peek(Token![=>]) {
70                    arms_content.parse::<Token![=>]>()?;
71                    break;
72                }
73
74                let tt: TokenTree = arms_content.parse()?;
75                tt.to_tokens(&mut pattern);
76            }
77
78            let body_content;
79            syn::braced!(body_content in arms_content);
80            let body = body_content.parse::<Template>()?;
81
82            arms.push((pattern, body));
83
84            if arms_content.peek(Token![,]) {
85                arms_content.parse::<Token![,]>()?;
86            }
87        }
88
89        Ok(Self {
90            span: Span::call_site(),
91            expr,
92            arms,
93        })
94    }
95}
96
97impl Expand for MatchNode {
98    fn expand(&self, output: &Ident, idents: &mut crate::ident::Iter) -> TokenStream {
99        let expr = &self.expr;
100        let arms: Vec<TokenStream> = self
101            .arms
102            .iter()
103            .map(|(pattern, body)| {
104                let body_expanded = body.expand(output, idents);
105                quote! { #pattern => { #body_expanded }}
106            })
107            .collect();
108
109        quote! {
110            match #expr {
111                #(#arms),*
112            }
113        }
114    }
115}