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
89
90
91
92
93
94
95
96
97
extern crate proc_macro;
extern crate proc_macro2;
extern crate quote;
extern crate syn;
use quote::quote;
use syn::parse_macro_input;
#[proc_macro_attribute]
pub fn auto_from(
_: proc_macro::TokenStream,
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!{
#item_enum
#(#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
}
}
}
}