1extern crate proc_macro;
2
3mod parse;
4
5use parse::Args;
6use proc_macro::TokenStream;
7use proc_macro2::Span;
8use proc_macro2::TokenStream as TokenStream2;
9use proc_macro_hack::proc_macro_hack;
10use quote::quote;
11
12#[proc_macro_hack]
13pub fn spez(tokens: TokenStream) -> TokenStream {
14 spez_impl(syn::parse_macro_input!(tokens)).into()
15}
16
17fn refs(n: usize) -> TokenStream2 {
18 let mut refs = TokenStream2::new();
19 for _ in 0..n {
20 refs.extend(quote![&]);
21 }
22 refs
23}
24
25fn spez_impl(args: Args) -> TokenStream2 {
26 let mut traits = TokenStream2::new();
27
28 let param_def = match args.param {
29 Some(param) => quote! {
30 let #param = self.0.take().unwrap();
31 },
32 None => quote! {},
33 };
34
35 let n_arms = args.arms.len();
36
37 for (i, arm) in args.arms.into_iter().enumerate() {
38 let name = syn::Ident::new(&format!("Match{}", i + 1), Span::call_site());
39 let body = arm.body;
40 let ty = arm.ty;
41 let generics = &arm.generics;
42 let where_clause = &arm.generics.where_clause;
43 let refs = refs(n_arms - i - 1);
44 let return_type = match arm.return_type {
45 Some(return_type) => quote! { #return_type },
46 None => quote! { () },
47 };
48
49 traits.extend(quote! {
50 trait #name {
51 type Return;
52 fn spez(&self) -> Self::Return;
53 }
54 impl #generics #name for #refs Match<#ty> #where_clause {
55 type Return = #return_type;
56 fn spez(&self) -> Self::Return {
57 #param_def
58 #body
59 }
60 }
61 });
62 }
63
64 let expr = args.expr;
65 let refs = refs(n_arms);
66
67 quote! {
68 {
69 struct Match<T>(core::cell::Cell<Option<T>>);
70 #traits
71 (#refs Match(core::cell::Cell::new(Some(#expr)))).spez()
72 }
73 }
74}