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 let generics = add_trait_bounds(input.generics);
16 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
17
18 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
42fn 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
71fn 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}