influxdb2_structmap_derive/
lib.rs1#![recursion_limit = "128"]
4
5extern crate proc_macro;
6
7use itertools::izip;
8use proc_macro::TokenStream;
9use quote::quote;
10use syn::{Data, DeriveInput, Ident};
11
12#[proc_macro_derive(FromMap)]
17pub fn from_map(input: TokenStream) -> TokenStream {
18 let ast = syn::parse_macro_input!(input as DeriveInput);
19
20 let fields = match ast.data {
22 Data::Struct(st) => st.fields,
23 _ => panic!("Implementation must be a struct"),
24 };
25 let idents: Vec<&Ident> = fields
26 .iter()
27 .filter_map(|field| field.ident.as_ref())
28 .collect::<Vec<&Ident>>();
29
30 let keys: Vec<String> = idents
32 .clone()
33 .iter()
34 .map(|ident| ident.to_string())
35 .collect::<Vec<String>>();
36
37 let typenames = fields
38 .iter()
39 .map(|field| { let t = field.ty.clone();
50 let s = quote! {#t}.to_string();
51 s
52 })
53 .collect::<Vec<String>>();
54
55 let name: &Ident = &ast.ident;
78 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
79
80 let datetime_re = regex::Regex::new(r"DateTime").unwrap();
81 let duration_re = regex::Regex::new(r"Duration").unwrap();
82 let base64_re = regex::Regex::new(r"Vec").unwrap();
83 let mut assignments = Vec::new();
84 for (key, typename, ident) in izip!(keys, typenames, idents) {
85 match &typename[..] {
86 "f64" => {
87 assignments.push(quote! {
88 let mut key = String::from(#key);
89 if !hashmap.contains_key(&key) {
90 key = format!("_{}", key);
91 }
92 match hashmap.entry(key) {
93 ::std::collections::btree_map::Entry::Occupied(entry) => {
94 if let Value::Double(v) = entry.get() {
95 settings.#ident = *v;
96 }
97 },
98 _ => panic!("Cannot parse out map entry"),
99 }
100 })
101 }
102 "i64" => {
103 assignments.push(quote! {
104 let mut key = String::from(#key);
105 if !hashmap.contains_key(&key) {
106 key = format!("_{}", key);
107 }
108 match hashmap.entry(key) {
109 ::std::collections::btree_map::Entry::Occupied(entry) => {
110 if let Value::Long(v) = entry.get() {
111 settings.#ident = *v;
112 }
113 },
114 _ => panic!("Cannot parse out map entry"),
115 }
116 })
117 }
118 "u64" => {
119 assignments.push(quote! {
120 let mut key = String::from(#key);
121 if !hashmap.contains_key(&key) {
122 key = format!("_{}", key);
123 }
124 match hashmap.entry(key) {
125 ::std::collections::btree_map::Entry::Occupied(entry) => {
126 if let Value::UnsignedLong(v) = entry.get() {
127 settings.#ident = *v;
128 }
129 },
130 _ => panic!("Cannot parse out map entry"),
131 }
132 })
133 }
134 "bool" => {
135 assignments.push(quote! {
136 let mut key = String::from(#key);
137 if !hashmap.contains_key(&key) {
138 key = format!("_{}", key);
139 }
140 match hashmap.entry(key) {
141 ::std::collections::btree_map::Entry::Occupied(entry) => {
142 if let Value::Bool(v) = entry.get() {
143 settings.#ident = *v;
144 }
145 },
146 _ => panic!("Cannot parse out map entry"),
147 }
148 })
149 }
150 "String" => {
151 assignments.push(quote! {
152 let mut key = String::from(#key);
153 if !hashmap.contains_key(&key) {
154 key = format!("_{}", key);
155 }
156 match hashmap.entry(key) {
157 ::std::collections::btree_map::Entry::Occupied(entry) => {
158 if let Value::String(v) = entry.get() {
159 settings.#ident = v.clone();
160 }
161 },
162 _ => panic!("Cannot parse out map entry"),
163 }
164 })
165 }
166 x if duration_re.is_match(x) => {
167 assignments.push(quote! {
168 let mut key = String::from(#key);
169 if !hashmap.contains_key(&key) {
170 key = format!("_{}", key);
171 }
172 match hashmap.entry(key) {
173 ::std::collections::btree_map::Entry::Occupied(entry) => {
174 if let Value::Duration(v) = entry.get() {
175 settings.#ident = *v;
176 }
177 },
178 _ => panic!("Cannot parse out map entry"),
179 }
180 })
181 }
182 x if datetime_re.is_match(x) => {
183 assignments.push(quote! {
184 let mut key = String::from(#key);
185 if !hashmap.contains_key(&key) {
186 key = format!("_{}", key);
187 }
188 match hashmap.entry(key) {
189 ::std::collections::btree_map::Entry::Occupied(entry) => {
190 if let Value::TimeRFC(v) = entry.get() {
191 settings.#ident = *v;
192 }
193 },
194 _ => panic!("Cannot parse out map entry"),
195 }
196 })
197 }
198 x if base64_re.is_match(x) => {
199 assignments.push(quote! {
200 let mut key = String::from(#key);
201 if !hashmap.contains_key(&key) {
202 key = format!("_{}", key);
203 }
204 match hashmap.entry(key) {
205 ::std::collections::btree_map::Entry::Occupied(entry) => {
206 if let Value::Base64Binary(v) = entry.get() {
207 settings.#ident = *v;
208 }
209 },
210 _ => panic!("Cannot parse out map entry"),
211 }
212 })
213 }
214 x => {
215 panic!("{} is not handled", x);
216 }
217 }
218 }
219
220 let tokens = quote! {
222 use influxdb2_structmap::value::Value;
223 use influxdb2_structmap::{GenericMap};
224
225 impl #impl_generics FromMap for #name #ty_generics #where_clause {
226
227 fn from_genericmap(mut hashmap: GenericMap) -> #name {
228 let mut settings = #name::default();
229
230 #(
231 #assignments
232 )*
233
234 settings
235 }
236
237 }
238 };
239 TokenStream::from(tokens)
240}
241
242#[cfg(test)]
243mod tests {
244 #[test]
245 fn ui() {
246 let t = trybuild::TestCases::new();
247 t.pass("tests/struct.rs");
248 }
249}
250