point_derive/
lib.rs

1/**
2* Copyright 2019 Comcast Cable Communications Management, LLC
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*
16* SPDX-License-Identifier: Apache-2.0
17*/
18extern crate proc_macro;
19
20use proc_macro::TokenStream;
21use proc_macro2::{Ident, Span};
22use quote::quote;
23use syn::{parse_macro_input, DeriveInput};
24
25#[proc_macro_derive(IntoPoint)]
26pub fn point_derive(input: TokenStream) -> TokenStream {
27    // Parse the string representation
28    let ast = parse_macro_input!(input as DeriveInput);
29
30    // Build the impl
31    let generated = impl_point(&ast, false);
32
33    // Return the generated impl
34    TokenStream::from(generated)
35}
36
37#[proc_macro_derive(IntoChildPoint)]
38pub fn child_point_derive(input: TokenStream) -> TokenStream {
39    // Parse the string representation
40    let ast = parse_macro_input!(input as DeriveInput);
41
42    // Build the impl
43    let generated = impl_point(&ast, true);
44
45    // Return the generated impl
46    TokenStream::from(generated)
47}
48
49fn impl_point(ast: &DeriveInput, child: bool) -> TokenStream {
50    let name = &ast.ident;
51    match ast.data {
52        syn::Data::Struct(ref data) => impl_struct_point_fields(name, &data.fields, child),
53        syn::Data::Enum(ref data) => {
54            println!("into_enum_point_fields called");
55            impl_enum_point_fields(name, &data.variants.iter().collect())
56        }
57        _ => unimplemented!(),
58    }
59}
60
61fn find_optional_type(field: syn::Field) -> Option<Ident> {
62    match field.clone().ty {
63        syn::Type::Path(p) => {
64            if let Some(i) = p.path.segments.clone().into_iter().next() {
65                match i.arguments {
66                    syn::PathArguments::AngleBracketed(a) => {
67                        //println!("{:?}", a);
68                        for ty in a.args {
69                            match ty {
70                                syn::GenericArgument::Type(p2) => match p2 {
71                                    syn::Type::Path(p2) => {
72                                        if let Some(i2) =
73                                            p2.path.segments.clone().into_iter().next()
74                                        {
75                                            return Some(i2.ident);
76                                        } else {
77                                            return None;
78                                        }
79                                    }
80                                    _ => return None,
81                                },
82                                _ => return None,
83                            }
84                        }
85                    }
86                    _ => {
87                        return None;
88                    }
89                }
90                return None;
91            } else {
92                return None;
93            }
94        }
95        _ => return None,
96    }
97}
98
99// TODO: Merge this with find_optional_type()
100fn find_optional_vec_type(field: syn::Field) -> Option<Ident> {
101    match field.clone().ty {
102        syn::Type::Path(p) => {
103            if let Some(i) = p.path.segments.clone().into_iter().next() {
104                match i.arguments {
105                    syn::PathArguments::AngleBracketed(a) => {
106                        for ty in a.args {
107                            match ty {
108                                syn::GenericArgument::Type(p2) => match p2 {
109                                    syn::Type::Path(p2) => {
110                                        if let Some(i2) =
111                                            p2.path.segments.clone().into_iter().next()
112                                        {
113                                            match i2.arguments {
114                                                syn::PathArguments::AngleBracketed(a2) => {
115                                                    for ty2 in a2.args {
116                                                        match ty2 {
117                                                            syn::GenericArgument::Type(p3) => {
118                                                                match p3 {
119                                                                    syn::Type::Path(p3) => {
120                                                                        if let Some(i3) = p3
121                                                                            .path
122                                                                            .segments
123                                                                            .clone()
124                                                                            .into_iter()
125                                                                            .next()
126                                                                        {
127                                                                            return Some(i3.ident);
128                                                                        } else {
129                                                                            return None;
130                                                                        }
131                                                                    }
132                                                                    _ => return None,
133                                                                }
134                                                            }
135                                                            _ => return None,
136                                                        }
137                                                    } // ends for ty2
138                                                }
139                                                _ => return None,
140                                            }
141                                            return None;
142                                        } else {
143                                            return None;
144                                        }
145                                    }
146                                    _ => return None,
147                                },
148                                _ => return None,
149                            }
150                        }
151                    }
152                    _ => {
153                        return None;
154                    }
155                }
156                return None;
157            } else {
158                return None;
159            }
160        }
161        _ => return None,
162    }
163}
164
165fn impl_struct_point_fields(name: &syn::Ident, fields: &syn::Fields, child: bool) -> TokenStream {
166    let _bool: Ident = Ident::new("bool", Span::call_site());
167    let bwc: Ident = Ident::new("BWC", Span::call_site());
168    let f_64: Ident = Ident::new("f64", Span::call_site());
169    let i_32: Ident = Ident::new("i32", Span::call_site());
170    let i_64: Ident = Ident::new("i64", Span::call_site());
171    let optional: Ident = Ident::new("Option", Span::call_site());
172    let s: Ident = Ident::new("String", Span::call_site());
173    let u_8: Ident = Ident::new("u8", Span::call_site());
174    let u_16: Ident = Ident::new("u16", Span::call_site());
175    let u_64: Ident = Ident::new("u64", Span::call_site());
176    let uuid: Ident = Ident::new("Uuid", Span::call_site());
177    let value: Ident = Ident::new("Value", Span::call_site());
178    let _vec: Ident = Ident::new("Vec", Span::call_site());
179
180    let mut result = Vec::new();
181    for field in fields {
182        let ident = &field.ident;
183        let ident_type = match field.clone().ty {
184            syn::Type::Path(p) => {
185                if let Some(i) = p.path.segments.clone().into_iter().next() {
186                    Some(i.ident)
187                } else {
188                    None
189                }
190            }
191            _ => None,
192        };
193
194        // In the case of optional types like Option<String> we need to
195        // find the second parameter or we won't know what to do below
196        let angle_type: Option<Ident> = if let Some(i_type) = ident_type.clone() {
197            if i_type == optional {
198                find_optional_type(field.clone())
199            } else {
200                None
201            }
202        } else {
203            None
204        };
205
206        let vec_angle_type: Option<Ident> = if let Some(i_type) = ident_type.clone() {
207            if i_type == _vec {
208                find_optional_type(field.clone())
209            } else {
210                None
211            }
212        } else {
213            None
214        };
215
216        match ident_type {
217            Some(i_type) => {
218                if i_type == bwc {
219                    result.push(quote! {
220                        p.add_field(stringify!(#ident), TsValue::Long(self.#ident.average()));
221                        p.add_field(format!("{}_total_weight_in_kb",stringify!(#ident)), TsValue::Long(self.#ident.total_weight_in_kb));
222                        p.add_field(format!("{}_num_seconds",stringify!(#ident)), TsValue::Long(self.#ident.num_seconds));
223                        p.add_field(format!("{}_num_occured",stringify!(#ident)), TsValue::Long(self.#ident.num_occured));
224                    });
225                } else if i_type == s {
226                    result.push(quote! {
227                        if !self.#ident.is_empty(){
228                            p.add_tag(stringify!(#ident), TsValue::String(self.#ident.clone()));
229                        }
230                    });
231                } else if i_type == i_32 {
232                    result.push(quote! {
233                        p.add_field(stringify!(#ident), TsValue::Integer(self.#ident));
234                    });
235                } else if i_type == i_64 {
236                    result.push(quote! {
237                        p.add_field(stringify!(#ident), TsValue::SignedLong(self.#ident));
238                    });
239                } else if i_type == uuid {
240                    result.push(quote! {
241                        p.add_field(stringify!(#ident), TsValue::String(self.#ident.to_string()));
242                    });
243                } else if i_type == u_8 {
244                    result.push(quote! {
245                        p.add_field(stringify!(#ident), TsValue::Byte(self.#ident));
246                    });
247                } else if i_type == u_16 {
248                    result.push(quote! {
249                        p.add_field(stringify!(#ident), TsValue::Short(self.#ident));
250                    });
251                } else if i_type == u_64 {
252                    result.push(quote! {
253                        p.add_field(stringify!(#ident), TsValue::Long(self.#ident));
254                    });
255                } else if i_type == f_64 {
256                    result.push(quote! {
257                        p.add_field(stringify!(#ident), TsValue::Float(self.#ident));
258                    });
259                } else if i_type == _bool {
260                    result.push(quote! {
261                        p.add_field(stringify!(#ident), TsValue::Boolean(self.#ident));
262                    });
263                } else if i_type == _vec {
264                    match &vec_angle_type {
265                        Some(ref vec_type) => {
266                            if *vec_type == s {
267                                result.push(quote! {
268                                    p.add_tag(stringify!(#ident), TsValue::StringVec(
269                                        self.#ident.clone()
270                                    ));
271                                });
272                            } else if *vec_type == u_64 {
273                                result.push(quote! {
274                                    p.add_tag(stringify!(#ident), TsValue::LongVec(
275                                        self.#ident.clone()
276                                    ));
277                                });
278                            } else if *vec_type == uuid {
279                                result.push(quote! {
280                                    p.add_tag(stringify!(#ident), TsValue::StringVec(
281                                        self.#ident.iter().map(|i| i.to_string()).collect::<Vec<String>>(),
282                                    ));
283                                });
284                            } else {
285                                //println!("vec found {} with inner: {:?}", i_type, vec_angle_type);
286                            }
287                        }
288                        None => {
289                            // Unable to identify this type
290                            println!(
291                                "Unable to identify vec type for {:?} {:?} {:?}",
292                                ident, i_type, vec_angle_type
293                            );
294                        }
295                    }
296                } else if i_type == optional {
297                    //println!("optional type: {:?} {:?} {:?}", ident, i_type, angle_type,);
298                    match angle_type {
299                        Some(option_type) => {
300                            if option_type == s {
301                                result.push(quote! {
302                                    if let Some(ref s) = self.#ident{
303                                        if !s.is_empty(){
304                                            p.add_tag(stringify!(#ident),
305                                                TsValue::String(s.clone()));
306                                        }
307                                    }
308                                });
309                            } else if option_type == _bool {
310                                result.push(quote! {
311                                    if self.#ident.is_some(){
312                                        p.add_field(stringify!(#ident),
313                                            TsValue::Boolean(self.#ident.unwrap()));
314                                    }
315                                });
316                            } else if option_type == bwc {
317                                result.push(quote! {
318                                    if self.#ident.is_some(){
319                                        let bwc_val = self.#ident.clone().unwrap();
320                                        p.add_field(stringify!(#ident),
321                                            TsValue::Long(bwc_val.average()));
322                                    }
323                                });
324                            } else if option_type == i_32 {
325                                result.push(quote! {
326                                    if self.#ident.is_some(){
327                                        p.add_field(stringify!(#ident),
328                                            TsValue::Integer(self.#ident.unwrap()));
329                                    }
330                                });
331                            } else if option_type == i_64 {
332                                result.push(quote! {
333                                    if self.#ident.is_some(){
334                                        p.add_field(stringify!(#ident),
335                                            TsValue::SignedLong(self.#ident.unwrap()));
336                                    }
337                                });
338                            } else if option_type == uuid {
339                                result.push(quote! {
340                                    if self.#ident.is_some(){
341                                        p.add_field(stringify!(#ident),
342                                            TsValue::String(self.#ident.unwrap().to_string()));
343                                    }
344                                });
345                            } else if option_type == u_64 {
346                                result.push(quote! {
347                                    if self.#ident.is_some(){
348                                        p.add_field(stringify!(#ident),
349                                            TsValue::Long(self.#ident.unwrap()));
350                                    }
351                                });
352                            } else if option_type == f_64 {
353                                result.push(quote! {
354                                    if self.#ident.is_some(){
355                                        p.add_field(stringify!(#ident),
356                                            TsValue::Float(self.#ident.unwrap()));
357                                    }
358                                });
359                            } else if option_type == _vec {
360                                let inner_vec_angle_type: Option<Ident> =
361                                    find_optional_vec_type(field.clone());
362                                match &inner_vec_angle_type {
363                                    Some(ref vec_type) => {
364                                        if *vec_type == s {
365                                            result.push(quote! {
366                                                if self.#ident.is_some() {
367                                            p.add_field(stringify!(#ident), TsValue::StringVec(self.#ident.clone().unwrap()));
368                                                }
369                                });
370                                        } // TODO: add other types here
371                                    }
372                                    None => {
373                                        // Unable to identify this type
374                                        println!(
375                                            "Unable to identify vec type for option_type = {:?} ident= {:?} i_type= {:?} vec_angle_type = {:?}",
376                                            option_type, ident, i_type, inner_vec_angle_type
377                                        );
378                                    }
379                                }
380                            } else {
381                                //println!("optional else: {:?}", option_type);
382                            }
383                        }
384                        None => {
385                            // Unable to identify this type
386                            println!(
387                                "Unable to identify optional type for {:?} {:?} {:?}",
388                                ident, i_type, angle_type
389                            );
390                        }
391                    }
392                } else {
393                    // Uncomment me to debug why some fields may be missing
394                    //println!("else: {:?} {:?} {:?}", ident, i_type, field.clone().ty);
395                }
396            }
397            None => {
398                // Unable to identify this type
399                println!("Unable to identify type for {:?}", ident);
400            }
401        }
402    }
403    if child {
404        TokenStream::from(quote! {
405            impl ChildPoint for #name {
406                fn sub_point(&self, p: &mut TsPoint) {
407                    #(#result)*
408                }
409            }
410        })
411    } else {
412        TokenStream::from(quote! {
413            impl IntoPoint for #name {
414                fn into_point(&self, name: Option<&str>, is_time_series: bool) -> Vec<TsPoint> {
415                    let mut p = TsPoint::new(name.unwrap_or("unknown"), is_time_series);
416                    #(#result)*
417                    vec![p]
418                }
419            }
420        })
421    }
422}
423
424fn impl_enum_point_fields(name: &syn::Ident, variants: &Vec<&syn::Variant>) -> TokenStream {
425    let mut result = Vec::new();
426    for variant in variants {
427        let ident = &variant.ident;
428        match variant.discriminant {
429            Some((ref _eq, ref expr)) => {
430                result.push(quote! {
431                    &#name::#ident => #expr.into_point(&mut buff),
432                });
433            }
434            None => {
435                result.push(quote! {
436                    &#name::#ident => #ident.into_point(&mut buff),
437                });
438            }
439        }
440    }
441    TokenStream::from(quote! {
442        impl IntoPoint for #name {
443            fn into_point(&self, name: Option<&str>, is_time_series: bool) -> TsPoint {
444                let mut p = TsPoint::new(point_name.unwrap_or("unknown"), is_time_series);
445                match self {
446                    #(#result)*
447                }
448                p
449            }
450        }
451    })
452}