Skip to main content

spez_macros/
lib.rs

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}