1use proc_macro::{self, TokenStream};
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput};
4
5
6#[proc_macro_derive(Map2Struct)]
7pub fn derive(input: TokenStream) -> TokenStream {
8 let DeriveInput { ident, data, .. } = parse_macro_input!(input);
9 if let syn::Data::Struct(data) = data {
10 if let syn::Fields::Named(fields) = data.fields {
11
12 let extractors = fields.named.iter().map(|field| {
14 let name = &field.ident;
15 let ty = &field.ty;
16 quote! {
17 let #name = map
18 .remove(stringify!(#name))
19 .ok_or_else(|| ::map2struct::Error::MissingField(stringify!(#name).to_string()))?
20 .parse::<#ty>()
21 .map_err(|e| ::map2struct::Error::FieldConversion(stringify!(#name).to_string(), e.into()))?;
22 }
23 });
24
25 let inserters = fields.named.iter().map(|field| {
27 let name = &field.ident;
28 quote!{#name}
29 });
30
31 let output = quote! {
33 impl ::map2struct::Map2Struct for #ident {
34 fn from_map(mut map: ::std::collections::HashMap<String, String>) -> ::map2struct::Result<Self> {
35 #(#extractors)*
39 if !map.is_empty() {
40 return Err(::map2struct::Error::ExtraFields(map.keys().cloned().collect()));
41 }
42 let t = Self {
43 #(#inserters,)*
44 };
45 Ok(t)
46 }
47 }
48 };
49
50 return output.into()
51 }
52 }
53 TokenStream::from(
54 syn::Error::new(
55 ident.span(),
56 "Only structs with named fields can derive `Map2Struct`"
57 ).to_compile_error()
58 )
59}