1#![recursion_limit = "128"]
4
5extern crate proc_macro;
6
7use proc_macro::TokenStream;
8use proc_macro2::Span;
9
10use quote::quote;
11use syn::{Data, DeriveInput, Fields, Ident, Type};
12
13use std::collections::BTreeMap;
14
15const RENAME_ERROR_MSG: &str = "Must be `#[rename(name = 'VALUE')]`";
16
17#[proc_macro_derive(FromMap)]
21pub fn from_map(input: TokenStream) -> TokenStream {
22 let ast = syn::parse_macro_input!(input as DeriveInput);
23
24 let fields = match ast.data {
26 Data::Struct(st) => st.fields,
27 _ => panic!("Implementation must be a struct"),
28 };
29 let idents: Vec<&Ident> = fields
30 .iter()
31 .filter_map(|field| field.ident.as_ref())
32 .collect::<Vec<&Ident>>();
33
34 let keys: Vec<String> = idents
36 .clone()
37 .iter()
38 .map(|ident| ident.to_string())
39 .collect::<Vec<String>>();
40
41 let typecalls: Vec<Ident> = fields
43 .iter()
44 .map(|field| match field.ty.clone() {
45 Type::Path(typepath) => {
46 let typename: String = quote! {#typepath}.to_string();
52
53 Ident::new(&typename, Span::mixed_site())
55 }
56 _ => unimplemented!(),
57 })
58 .collect::<Vec<Ident>>();
59
60 let name: &Ident = &ast.ident;
62 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
63
64 let tokens = quote! {
66 use structmap::value::Value;
67 use structmap::{StringMap, GenericMap};
68
69 impl #impl_generics FromMap for #name #ty_generics #where_clause {
70
71 fn from_stringmap(mut hashmap: StringMap) -> #name {
72 let mut settings = #name::default();
73 #(
74 match hashmap.entry(String::from(#keys)) {
75 ::std::collections::btree_map::Entry::Occupied(entry) => {
76 let value = match entry.get().parse::<#typecalls>() {
77 Ok(val) => val,
78 _ => panic!("Cannot parse out map entry")
79 };
80 settings.#idents = value;
81 },
82 _ => ()
83 }
84 )*
85 settings
86 }
87
88 fn from_genericmap(mut hashmap: GenericMap) -> #name {
89 let mut settings = #name::default();
90 #(
91 match hashmap.entry(String::from(#keys)) {
92 ::std::collections::btree_map::Entry::Occupied(entry) => {
93 let value = match entry.get().#typecalls() {
95 Some(val) => val,
96 None => panic!("Cannot parse out map entry")
97 };
98 settings.#idents = value;
99 },
100 _ => (),
101 }
102 )*
103 settings
104 }
105 }
106 };
107 TokenStream::from(tokens)
108}
109
110#[proc_macro_derive(ToMap, attributes(rename))]
113pub fn to_map(input_struct: TokenStream) -> TokenStream {
114 let ast = syn::parse_macro_input!(input_struct as DeriveInput);
115
116 let fields = match ast.data {
118 Data::Struct(st) => st.fields,
119 _ => panic!("Implementation must be a struct"),
120 };
121
122 let rename_map = parse_rename_attrs(&fields);
124
125 let idents: Vec<&Ident> = fields
127 .iter()
128 .filter_map(|field| field.ident.as_ref())
129 .collect::<Vec<&Ident>>();
130
131 let keys: Vec<String> = idents
133 .clone()
134 .iter()
135 .map(|ident| ident.to_string())
136 .map(|name| match rename_map.contains_key(&name) {
137 true => rename_map.get(&name).unwrap().clone(),
138 false => name,
139 })
140 .collect::<Vec<String>>();
141
142 let name: &Ident = &ast.ident;
144 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
145
146 let tokens = quote! {
148
149 impl #impl_generics ToMap for #name #ty_generics #where_clause {
150
151 fn to_stringmap(mut input_struct: #name) -> structmap::StringMap {
152 let mut map = structmap::StringMap::new();
153 #(
154 map.insert(#keys.to_string(), input_struct.#idents.to_string());
155 )*
156 map
157 }
158
159 fn to_genericmap(mut input_struct: #name) -> structmap::GenericMap {
160 let mut map = structmap::GenericMap::new();
161 #(
162 map.insert(#keys.to_string(), structmap::value::Value::new(input_struct.#idents));
163 )*
164 map
165 }
166 }
167 };
168 TokenStream::from(tokens)
169}
170
171fn parse_rename_attrs(fields: &Fields) -> BTreeMap<String, String> {
175 let mut rename: BTreeMap<String, String> = BTreeMap::new();
176
177 if let Fields::Named(_) = fields {
179 } else {
181 panic!("Must have named fields.");
182 }
183
184 for field in fields.iter() {
186 for attr in field.attrs.iter() {
187 let field_name = field.ident.as_ref().unwrap().to_string();
189 if rename.contains_key(&field_name) {
190 panic!("Cannot redefine field name multiple times.");
191 }
192
193 match attr.parse_meta() {
196 Ok(syn::Meta::List(lst)) => {
197 match lst.nested.first() {
199 Some(syn::NestedMeta::Meta(syn::Meta::NameValue(nm))) => {
200 let path = nm.path.get_ident().unwrap().to_string();
202 if path != "name" {
203 panic!("{}", RENAME_ERROR_MSG);
204 }
205
206 let lit = match &nm.lit {
207 syn::Lit::Str(val) => val.value(),
208 _ => {
209 panic!("{}", RENAME_ERROR_MSG);
210 }
211 };
212 rename.insert(field_name, lit);
213 }
214 _ => {
215 panic!("{}", RENAME_ERROR_MSG);
216 }
217 }
218 }
219 _ => {
220 panic!("{}", RENAME_ERROR_MSG);
221 }
222 }
223 }
224 }
225 rename
226}