firebae_derive/
lib.rs

1extern crate proc_macro;
2use self::proc_macro::TokenStream;
3
4use quote::quote;
5use syn::{parse_macro_input, parse_quote, DeriveInput, Fields, GenericParam, Generics};
6
7fn add_trait_bounds(mut generics: Generics) -> Generics {
8    for param in &mut generics.params {
9        if let GenericParam::Type(ref mut type_param) = *param {
10            type_param.bounds.push(parse_quote!(FirebaseMapValue));
11        }
12    }
13    generics
14}
15
16fn error(span: proc_macro2::Span, message: &str) -> proc_macro2::TokenStream {
17    syn::Error::new(span, message).into_compile_error()
18}
19
20#[proc_macro_derive(AsFirebaseMap)]
21pub fn impl_as_firebase_map(input: TokenStream) -> TokenStream {
22    let ast = parse_macro_input!(input as DeriveInput);
23    let span = proc_macro2::Span::call_site();
24
25    let name = &ast.ident;
26    let generics = add_trait_bounds(ast.generics);
27    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
28
29    let data = match ast.data {
30        syn::Data::Struct(data) => data,
31        _ => return error(span, "AsFirebaseMap should be called on a struct").into(),
32    };
33
34    let fields = match data.fields {
35        Fields::Named(fields) => fields.named,
36        _ => return error(span, "AsFirebaseMap only works on named fields").into(),
37    };
38
39    let inserts = fields.into_iter().map(|f| {
40        let name = f.ident.unwrap();
41
42        quote! {
43            h.insert(stringify!(#name), &self.#name);
44        }
45    });
46
47    let mod_name = syn::Ident::new(
48        &format!("__impl_as_firebase_map_{}", name),
49        proc_macro2::Span::call_site(),
50    );
51
52    TokenStream::from(quote! {
53        mod #mod_name {
54            use firebae_cm::{
55                IntoFirebaseMap,
56                FirebaseMap,
57                FirebaseMapValue,
58            };
59            use super::{#name};
60
61            impl #impl_generics IntoFirebaseMap for #name #ty_generics #where_clause {
62                fn as_map(&self) -> FirebaseMap {
63                    let mut h = FirebaseMap::new();
64                    #(#inserts)*
65                    h
66                }
67            }
68        }
69    })
70}