map2struct_derive/
lib.rs

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            // Step 1: build extractors
13            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            // Step 2: Build builder
26            let inserters = fields.named.iter().map(|field| {
27                let name = &field.ident;
28                quote!{#name}
29            });
30
31            // Step 3: Build implementation
32            let output = quote! {
33                impl ::map2struct::Map2Struct for #ident {
34                    fn from_map(mut map: ::std::collections::HashMap<String, String>) -> ::map2struct::Result<Self> {
35                        // Try to extract every field; raise error on option
36                        // Check that map is empty
37                        // Build and return result
38                        #(#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}