from_enum_derive/
lib.rs

1extern crate proc_macro;
2
3mod expand {
4    use proc_macro2::TokenStream;
5    use quote::quote;
6    use syn::{Data, DataEnum, DeriveInput, Error, Result};
7
8    pub(crate) fn derive(input: &DeriveInput) -> Result<TokenStream> {
9        match &input.data {
10            Data::Enum(data) => impl_enum(input, data),
11            _ => Err(Error::new_spanned(
12                input,
13                "From can only be derived for enums",
14            )),
15        }
16    }
17
18    fn impl_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
19        let ty = &input.ident;
20        let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
21
22        let from_impls = data
23            .variants
24            .iter()
25            .filter(|variant| variant.fields.len() == 1)
26            .map(|variant| {
27                let variant_ident = &variant.ident;
28                let field = &variant.fields.iter().next().unwrap();
29                let field_ty = &field.ty;
30                let field_has_from = field.attrs.iter().any(|attr| attr.path.is_ident("from"));
31
32                if field_has_from {
33                    Some(quote! {
34                        impl #impl_generics From<#field_ty> for #ty #ty_generics #where_clause {
35                            fn from(value: #field_ty) -> Self {
36                                #ty::#variant_ident(value)
37                            }
38                        }
39                    })
40                } else {
41                    None
42                }
43            });
44
45        Ok(quote! {
46            #(#from_impls)*
47        })
48    }
49}
50
51use proc_macro::TokenStream;
52use syn::{parse_macro_input, DeriveInput};
53
54#[proc_macro_derive(From, attributes(from))]
55pub fn derive_from(input: TokenStream) -> TokenStream {
56    let input = parse_macro_input!(input as DeriveInput);
57    expand::derive(&input)
58        .unwrap_or_else(|err| err.to_compile_error())
59        .into()
60}