md_match_derive/
lib.rs

1//! # MdMatch
2//! This crate provides the derive macro for md-match.
3//! Please refer to https://github.com/Mu001999/md-match for how to set this up.
4
5use proc_macro::TokenStream;
6use proc_macro2::Span;
7
8/// Derive MdMatch for Enum
9#[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    // clone generics as ref_generics and add lifetime 'mdmatch
18    let mut ref_generics = generics.clone();
19    ref_generics.params.push(syn::parse_quote!('mdmatch));
20    // get the impl_generics of ref_generics
21    let (ref_impl_generics, _, _) = ref_generics.split_for_impl();
22
23    // get the element type, if no element, return token stream with error
24    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    // impl MdMatch for Enum, &Enum and &mut Enum
53    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}