1extern crate proc_macro2;
2use proc_macro2::TokenStream;
3use quote::{quote, quote_spanned};
4use syn::{
5 parse_macro_input, parse_quote, spanned::Spanned, Data, DeriveInput, Fields, GenericParam,
6 Generics,
7};
8
9#[proc_macro_derive(Recipe)]
11pub fn derive_bakery(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
12 let input = parse_macro_input!(input as DeriveInput);
15 let name = input.ident;
16 let generics = add_trait_bounds(input.generics);
18 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
19 let fields = recipe_token_stream(&input.data);
20
21 let expanded = quote! {
22 impl #impl_generics bakery::Recipe for #name #ty_generics #where_clause {
23 fn recipe(tree: &mut bakery::NodeTree) -> u32 {
24 let nid = tree.create_struct(None, "S");
25 #fields
26 nid
27 }
28 }
29 };
30
31 proc_macro::TokenStream::from(expanded)
32}
33
34fn add_trait_bounds(mut generics: Generics) -> Generics {
36 for param in &mut generics.params {
37 if let GenericParam::Type(ref mut type_param) = *param {
38 type_param.bounds.push(parse_quote!(bakery::Recipe));
39 }
40 }
41 generics
42}
43
44fn recipe_token_stream(data: &Data) -> TokenStream {
45 match *data {
46 Data::Struct(ref data) => {
47 match data.fields {
48 Fields::Named(ref fields) => {
49 let quotes = fields.named.iter().map(|f| {
50 let name = &f.ident;
51 let ty = &f.ty;
52 quote_spanned! {
53 f.span() =>
54 let nid_ty = <#ty> :: recipe(tree);
56 tree.create_struct_member(nid, stringify!(#name), nid_ty);
57 }
58 });
59 quote! {
60 #( #quotes )*
61 }
62 }
63 Fields::Unnamed(_) | Fields::Unit => unimplemented!(),
64 }
65 }
66 Data::Enum(_) | Data::Union(_) => unimplemented!(),
67 }
68}