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}