1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//! This library allows you to derive the `From` trait for enums. It is used
//! to automatically generate `std::convert::From` implementations for enum
//! variants containing only one field. It currently works for tuple or struct
//! variants. Here is a small example:
//!
//! ```
//! use auto_from::From;
//!
//! #[derive(Debug, PartialEq, From)]
//! enum Foo {
//!     Int(i32),
//!     Long { value: i64 },
//! }
//!
//! assert_eq!(Foo::Int(24), Foo::from(24i32));
//! assert_eq!(Foo::Long{ value: 24 }, Foo::from(24i64));
//! ```
//!
//! This should be most useful when constructing error enums that require
//! writing many `From` implementations, or using many
//! `.map_error(|e| MyError::Variant(e))` calls across the code. This crate
//! just simplifies the process.

extern crate proc_macro;

use quote::quote;
use syn::parse_macro_input;

#[proc_macro_derive(From)]
pub fn derive_from(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let item_enum = parse_macro_input!(item as syn::ItemEnum);

    let variants: Vec<_> = item_enum
        .variants
        .iter()
        .map(|variant| build_from_variant(variant, &item_enum.ident))
        .collect();

    let syntax = quote! {
        #(#variants)*
    };

    syntax.into()
}

fn build_from_variant(
    variant: &syn::Variant,
    enum_type: &syn::Ident,
) -> Option<proc_macro2::TokenStream> {
    let field = match &variant.fields {
        syn::Fields::Named(named) => match named.named.first() {
            Some(ref pair) if named.named.len() == 1 => pair.value().clone(),
            _ => return None,
        },
        syn::Fields::Unnamed(unnamed) => match unnamed.unnamed.first() {
            Some(ref pair) if unnamed.unnamed.len() == 1 => pair.value().clone(),
            _ => return None,
        },
        syn::Fields::Unit => return None,
    };

    Some(build_from_field(&field, &variant.ident, enum_type))
}

fn build_from_field(
    field: &syn::Field,
    variant_name: &syn::Ident,
    enum_type: &syn::Ident,
) -> proc_macro2::TokenStream {
    let from_type = &field.ty;

    let variant = match &field.ident {
        Some(ident) => quote! {
            { #ident: x }
        },
        None => quote! {
            (x)
        },
    };

    quote! {
        impl From<#from_type> for #enum_type {
            fn from(x: #from_type) -> Self {
                #enum_type::#variant_name #variant
            }
        }
    }
}