1use proc_macro::TokenStream;
6use proc_macro2::Span;
7
8#[proc_macro_derive(MdMatch)]
10pub fn derive_md_match(item: TokenStream) -> TokenStream {
11 let r#enum = syn::parse_macro_input!(item as syn::ItemEnum);
12
13 let name = r#enum.ident;
14 let generics = r#enum.generics;
15 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
16
17 let mut ref_generics = generics.clone();
19 ref_generics.params.push(syn::parse_quote!('mdmatch));
20 let (ref_impl_generics, _, _) = ref_generics.split_for_impl();
22
23 let elem_type = match r#enum
25 .variants
26 .iter()
27 .map(|variant| {
28 let fields = &variant.fields;
29 if fields.len() == 1 {
30 let field = fields.iter().next().unwrap();
31 let ty = &field.ty;
32 Some(ty)
33 } else {
34 None
35 }
36 })
37 .next()
38 .flatten()
39 {
40 Some(ty) => ty,
41 None => {
42 return syn::Error::new(Span::call_site(), "requires one element")
43 .to_compile_error()
44 .into()
45 }
46 };
47
48 let variant = r#enum.variants.iter().map(|variant| &variant.ident);
49 let variant_ref = variant.clone();
50 let variant_mut_ref = variant.clone();
51
52 let impl_md_match = quote::quote! {
54 impl #impl_generics MdMatch for #name #ty_generics #where_clause {
55 type Elem = #elem_type;
56 fn md_match<R>(self, f: impl FnOnce(Self::Elem) -> R) -> R {
57 match self {
58 #(#name::#variant(x) => f(x)),*
59 }
60 }
61 }
62
63 impl #ref_impl_generics MdMatch for &'mdmatch #name #ty_generics #where_clause {
64 type Elem = &'mdmatch #elem_type;
65 fn md_match<R>(self, f: impl FnOnce(Self::Elem) -> R) -> R {
66 match self {
67 #(#name::#variant_ref(x) => f(x)),*
68 }
69 }
70 }
71
72 impl #ref_impl_generics MdMatch for &'mdmatch mut #name #ty_generics #where_clause {
73 type Elem = &'mdmatch mut #elem_type;
74 fn md_match<R>(self, f: impl FnOnce(Self::Elem) -> R) -> R {
75 match self {
76 #(#name::#variant_mut_ref(x) => f(x)),*
77 }
78 }
79 }
80 };
81
82 TokenStream::from(impl_md_match)
83}