1#![recursion_limit = "128"]
4
5extern crate proc_macro;
6mod expand_tuple;
7mod expand_writable;
8
9use expand_tuple::{make_tuple_fields, make_tuple_tags};
10use expand_writable::impl_writeable;
11use itertools::izip;
12use proc_macro::TokenStream;
13use quote::quote;
14use syn::{Data, DeriveInput, Ident};
15
16#[proc_macro_derive(FromDataPoint)]
21pub fn from_data_point(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
48 .clone()
49 .iter()
50 .map(|ident| ident.to_string())
51 .collect::<Vec<String>>();
52
53 let typenames = fields
55 .iter()
56 .map(|field| {
57 let t = field.ty.clone();
58 let s = quote! {#t}.to_string();
59 s
60 })
61 .collect::<Vec<String>>();
62
63 let name: &Ident = &ast.ident;
65 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
66
67 let datetime_re = regex::Regex::new(r"DateTime").unwrap();
68 let duration_re = regex::Regex::new(r"Duration").unwrap();
69 let base64_re = regex::Regex::new(r"Vec").unwrap();
70 let mut assignments = Vec::new();
71 for (key, typename, ident) in izip!(keys, typenames, idents) {
72 match &typename[..] {
73 "f64" => {
74 assignments.push(quote! {
75 let mut key = String::from(#key);
76 if !hashmap.contains_key(&key) {
77 key = format!("_{}", key);
78 }
79 match hashmap.entry(key.clone()) {
80 ::std::collections::btree_map::Entry::Occupied(entry) => {
81 if let influxdb2_structmap::value::Value::Double(v) = entry.get() {
82 settings.#ident = (v as &::num_traits::cast::ToPrimitive).to_f64().unwrap();
83 }
84 },
85 _ => panic!("Cannot parse out map entry, key: {}", key),
86 }
87 })
88 }
89 "i64" => {
90 assignments.push(quote! {
91 let mut key = String::from(#key);
92 if !hashmap.contains_key(&key) {
93 key = format!("_{}", key);
94 }
95 match hashmap.entry(key.clone()) {
96 ::std::collections::btree_map::Entry::Occupied(entry) => {
97 if let influxdb2_structmap::value::Value::Long(v) = entry.get() {
98 settings.#ident = *v;
99 }
100 },
101 _ => panic!("Cannot parse out map entry, key: {}", key),
102 }
103 })
104 }
105 "u64" => {
106 assignments.push(quote! {
107 let mut key = String::from(#key);
108 if !hashmap.contains_key(&key) {
109 key = format!("_{}", key);
110 }
111 match hashmap.entry(key.clone()) {
112 ::std::collections::btree_map::Entry::Occupied(entry) => {
113 if let influxdb2_structmap::value::Value::UnsignedLong(v) = entry.get() {
114 settings.#ident = *v;
115 }
116 },
117 _ => panic!("Cannot parse out map entry, key: {}", key),
118 }
119 })
120 }
121 "bool" => {
122 assignments.push(quote! {
123 let mut key = String::from(#key);
124 if !hashmap.contains_key(&key) {
125 key = format!("_{}", key);
126 }
127 match hashmap.entry(key.clone()) {
128 ::std::collections::btree_map::Entry::Occupied(entry) => {
129 if let influxdb2_structmap::value::Value::Bool(v) = entry.get() {
130 settings.#ident = *v;
131 }
132 },
133 _ => panic!("Cannot parse out map entry, key: {}", key),
134 }
135 })
136 }
137 "String" => {
138 assignments.push(quote! {
139 let mut key = String::from(#key);
140 if !hashmap.contains_key(&key) {
141 key = format!("_{}", key);
142 }
143 match hashmap.entry(key.clone()) {
144 ::std::collections::btree_map::Entry::Occupied(entry) => {
145 if let influxdb2_structmap::value::Value::String(v) = entry.get() {
146 settings.#ident = v.clone();
147 }
148 },
149 _ => panic!("Cannot parse out map entry, key: {}", key),
150 }
151 })
152 }
153 x if duration_re.is_match(x) => {
154 assignments.push(quote! {
155 let mut key = String::from(#key);
156 if !hashmap.contains_key(&key) {
157 key = format!("_{}", key);
158 }
159 match hashmap.entry(key.clone()) {
160 ::std::collections::btree_map::Entry::Occupied(entry) => {
161 if let influxdb2_structmap::value::Value::Duration(v) = entry.get() {
162 settings.#ident = *v;
163 }
164 },
165 _ => panic!("Cannot parse out map entry, key: {}", key),
166 }
167 })
168 }
169 x if datetime_re.is_match(x) => {
170 assignments.push(quote! {
171 let mut key = String::from(#key);
172 if !hashmap.contains_key(&key) {
173 key = format!("_{}", key);
174 }
175 match hashmap.entry(key.clone()) {
176 ::std::collections::btree_map::Entry::Occupied(entry) => {
177 if let influxdb2_structmap::value::Value::TimeRFC(v) = entry.get() {
178 settings.#ident = *v;
179 }
180 },
181 _ => panic!("Cannot parse out map entry, key: {}", key),
182 }
183 })
184 }
185 x if base64_re.is_match(x) => {
186 assignments.push(quote! {
187 let mut key = String::from(#key);
188 if !hashmap.contains_key(&key) {
189 key = format!("_{}", key);
190 }
191 match hashmap.entry(key.clone()) {
192 ::std::collections::btree_map::Entry::Occupied(entry) => {
193 if let influxdb2_structmap::value::Value::Base64Binary(v) = entry.get() {
194 settings.#ident = *v;
195 }
196 },
197 _ => panic!("Cannot parse out map entry, key: {}", key),
198 }
199 })
200 }
201 x => {
202 panic!("{} is not handled", x);
203 }
204 }
205 }
206
207 let tokens = quote! {
209 impl #impl_generics influxdb2_structmap::FromMap for #name #ty_generics #where_clause {
210
211 fn from_genericmap(mut hashmap: influxdb2_structmap::GenericMap) -> #name {
212 let mut settings = #name::default();
213
214 #(
215 #assignments
216 )*
217
218 settings
219 }
220
221 }
222 };
223 TokenStream::from(tokens)
224}
225
226#[proc_macro]
227pub fn impl_tuple_tags(tokens: TokenStream) -> TokenStream {
228 make_tuple_tags(tokens)
229}
230
231#[proc_macro]
232pub fn impl_tuple_fields(tokens: TokenStream) -> TokenStream {
233 make_tuple_fields(tokens)
234}
235
236#[proc_macro_derive(WriteDataPoint, attributes(measurement, influxdb))]
237pub fn impl_influx_writable(tokens: TokenStream) -> TokenStream {
238 impl_writeable(tokens)
239}
240
241#[cfg(test)]
242mod tests {
243 #[test]
244 fn ui() {
245 let t = trybuild::TestCases::new();
246 t.pass("tests/struct.rs");
247 t.pass("tests/multistruct.rs");
248 t.pass("tests/writable.rs")
249 }
250}