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
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(From, attributes(fieldwise))]
pub fn derive_fieldwise_from(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let DeriveInput {
ident: destination,
data,
generics,
attrs,
..
} = parse_macro_input!(input);
let struct_data = match data {
syn::Data::Struct(s) => s,
_ => panic!("Deriving fieldwise::From only works on structs"),
};
let named_fields = match struct_data.fields {
syn::Fields::Named(f) => f,
_ => panic!("Deriving fieldwise::From only works on structs with named fields"),
};
let field_idents = named_fields
.named
.into_iter()
.map(|field| field.ident.expect("Encountered a field without an ident"));
let from_ident: Ident = attrs
.into_iter()
.flat_map(|attr| attr.parse_meta().ok())
.flat_map(|meta| match meta {
syn::Meta::List(syn::MetaList { nested, path, .. }) if path.is_ident("fieldwise") => {
nested.into_iter()
}
_ => panic!("Unexpected format of #[fieldwise(from(X))]"),
})
.map(|nested_meta| match nested_meta {
syn::NestedMeta::Meta(meta) => meta,
_ => panic!("Unexpected format of #[fieldwise(from(X))]"),
})
.flat_map(|meta| match meta {
syn::Meta::List(syn::MetaList { nested, path, .. }) if path.is_ident("from") => {
nested.into_iter()
}
_ => panic!("Unexpected format of #[fieldwise(from(X))]"),
})
.map(|nested_meta| match nested_meta {
syn::NestedMeta::Meta(meta) => meta,
_ => panic!("Unexpected format of #[fieldwise(from(X))]"),
})
.map(|meta| match meta {
syn::Meta::Path(path) => path.get_ident().cloned().unwrap(),
_ => panic!("Unexpected format of #[fieldwise(from(X))]"),
})
.next()
.expect("Missing `#[fieldwise(from(X))]` attribute");
let source = from_ident;
let field_mappings: Vec<TokenStream> = field_idents
.map(|ident| {
quote! {
#ident: s.#ident.into()
}
})
.collect();
quote! {
impl #generics std::convert::From<#source> for #destination{
fn from(s: #source) -> #destination {
#destination {
#(#field_mappings),*
}
}
}
}
.into()
}