1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4 Attribute, Expr, Pat, Result as SynResult, Token, Type,
5 parse::{Parse, ParseStream},
6 parse_macro_input,
7};
8
9struct MatchWrapInput {
10 trait_type: Type,
11 expr: Expr,
12 arms: Vec<(Vec<Attribute>, Pat, Expr)>,
13}
14
15impl Parse for MatchWrapInput {
16 fn parse(input: ParseStream) -> SynResult<Self> {
17 let trait_type = input.parse()?;
18 input.parse::<Token![;]>()?;
19
20 let expr = input.parse()?;
21 input.parse::<Token![;]>()?;
22
23 let mut arms = Vec::new();
24 while !input.is_empty() {
25 let attrs = input.call(syn::Attribute::parse_outer)?;
26 let pat = Pat::parse_single(input)?;
27 input.parse::<Token![=>]>()?;
28 let arm_expr = input.parse()?;
29 arms.push((attrs, pat, arm_expr));
30
31 if input.peek(Token![,]) {
32 input.parse::<Token![,]>()?;
33 }
34 }
35
36 Ok(MatchWrapInput {
37 trait_type,
38 expr,
39 arms,
40 })
41 }
42}
43
44#[proc_macro]
45pub fn match_box(input: TokenStream) -> TokenStream {
46 const DIVERGE_ATTR: &str = "diverges";
47
48 let input = parse_macro_input!(input as MatchWrapInput);
49 let trait_type = &input.trait_type;
50 let expr = &input.expr;
51
52 let arms = input.arms.iter().map(|(attrs, pat, arm_expr)| {
53 let is_diverging = attrs.iter().any(|attr| attr.path().is_ident(DIVERGE_ATTR));
54
55 if is_diverging {
56 quote! {#pat => #arm_expr}
57 } else {
58 quote! { #pat => ::std::boxed::Box::new(#arm_expr) as ::std::boxed::Box<#trait_type> }
59 }
60 });
61
62 quote! { match #expr { #(#arms,)* } }.into()
63}