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}