blpapi_derive/
lib.rs

1extern crate proc_macro;
2
3use proc_macro2::TokenStream;
4use quote::{quote, quote_spanned};
5use syn::spanned::Spanned;
6use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields, GenericParam, Generics};
7
8#[proc_macro_derive(RefData)]
9pub fn derive_ref_data(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10    let input = parse_macro_input!(input as DeriveInput);
11
12    let name = input.ident;
13
14    // Add a bound `T: RefData` to every type parameter T.
15    let generics = add_trait_bounds(input.generics);
16    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
17
18    // Generate an expression to sum up the heap size of each field.
19    let fields = fields(&input.data);
20    let on_field = on_field(&input.data);
21
22    let expanded = quote! {
23        impl #impl_generics blpapi::ref_data::RefData for #name #ty_generics #where_clause {
24            #fields
25            #on_field
26        }
27    };
28    proc_macro::TokenStream::from(expanded)
29}
30
31fn add_trait_bounds(mut generics: Generics) -> Generics {
32    for param in &mut generics.params {
33        if let GenericParam::Type(ref mut type_param) = *param {
34            type_param
35                .bounds
36                .push(parse_quote!(blpapi::ref_data::RefData));
37        }
38    }
39    generics
40}
41
42/// fn on_field(...) {...}
43fn on_field(data: &Data) -> TokenStream {
44    match data {
45        Data::Struct(ref data) => match data.fields {
46            Fields::Named(ref fields) => {
47                let recurse = fields.named.iter().map(|f| {
48                    let name = &f.ident;
49                    let field = f.ident.as_ref().unwrap().to_string().to_uppercase();
50                    quote_spanned! {f.span()=>
51                        #field => if let Some(v) = element.get_at(0) {
52                            self.#name = v;
53                        },
54                    }
55                });
56                quote! {
57                    fn on_field(&mut self, field: &str, element: &blpapi::element::Element) {
58                        match field {
59                            #(#recurse)*
60                            _ => { dbg!("Unrecognized field '{}'", field); }
61                        }
62                    }
63                }
64            }
65            _ => unimplemented!(),
66        },
67        Data::Enum(_) | Data::Union(_) => unimplemented!(),
68    }
69}
70
71/// const FIELDS = ...
72fn fields(data: &Data) -> TokenStream {
73    match data {
74        Data::Struct(ref data) => match data.fields {
75            Fields::Named(ref fields) => {
76                let recurse = fields.named.iter().map(|f| {
77                    let field = f.ident.as_ref().unwrap().to_string().to_uppercase();
78                    quote_spanned! {f.span()=> #field }
79                });
80                quote! {
81                    const FIELDS: &'static [&'static str] = &[#(#recurse),*];
82                }
83            }
84            _ => unimplemented!(),
85        },
86        Data::Enum(_) | Data::Union(_) => unimplemented!(),
87    }
88}