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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
extern crate proc_macro;
use quote::quote;
use syn::parse_macro_input;
#[proc_macro_derive(From, attributes(auto_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> {
for attr in &variant.attrs {
match attr.parse_meta() {
Ok(syn::Meta::List(list)) => {
if list.ident == "auto_from" {
if let Some(nested) = list.nested.first() {
match nested.value() {
syn::NestedMeta::Meta(syn::Meta::Word(ident)) => {
if ident == "skip" {
return None;
}
return Some(quote! {
compile_error!(concat!("Unknown name in #[auto_from] attr: ", stringify!(#ident)));
});
}
_ => {
return Some(quote! {
compile_error!("Unknown structure of #[auto_from] attr");
});
}
}
}
}
}
_ => {}
}
}
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
}
}
}
}