1use proc_macro::TokenStream;
8use proc_macro2::TokenStream as TokenStream2;
9use quote::quote;
10use syn::{
11 parse::{Parse, ParseStream},
12 parse_macro_input,
13 punctuated::Punctuated,
14 DeriveInput, Ident, Path, PathSegment, Token,
15};
16use synstructure::{AddBounds, Structure};
17
18#[proc_macro_derive(Bake, attributes(databake))]
35pub fn bake_derive(input: TokenStream) -> TokenStream {
36 let input = parse_macro_input!(input as DeriveInput);
37 TokenStream::from(bake_derive_impl(&input))
38}
39
40fn bake_derive_impl(input: &DeriveInput) -> TokenStream2 {
41 let mut structure = Structure::new(input);
42
43 struct PathAttr(Punctuated<PathSegment, Token![::]>);
44
45 impl Parse for PathAttr {
46 fn parse(input: ParseStream<'_>) -> syn::parse::Result<Self> {
47 let i: Ident = input.parse()?;
48 if i != "path" {
49 return Err(input.error(format!("expected token \"path\", found {i:?}")));
50 }
51 input.parse::<Token![=]>()?;
52 Ok(Self(input.parse::<Path>()?.segments))
53 }
54 }
55
56 let path = input
57 .attrs
58 .iter()
59 .find(|a| a.path().is_ident("databake"))
60 .expect("missing databake(path = ...) attribute")
61 .parse_args::<PathAttr>()
62 .unwrap()
63 .0;
64
65 let bake_body = structure.each_variant(|vi| {
66 let recursive_calls = vi.bindings().iter().map(|b| {
67 let ident = b.binding.clone();
68 quote! { let #ident = #ident.bake(env); }
69 });
70
71 let constructor = vi.construct(|_, i| {
72 let ident = &vi.bindings()[i].binding;
73 quote! { # #ident }
74 });
75
76 quote! {
77 #(#recursive_calls)*
78 databake::quote! { #path::#constructor }
79 }
80 });
81
82 let borrows_size_body = structure.each_variant(|vi| {
83 let recursive_calls = vi.bindings().iter().map(|b| {
84 let ident = b.binding.clone();
85 quote! { #ident.borrows_size() }
86 });
87
88 quote! {
89 0 #(+ #recursive_calls)*
90 }
91 });
92
93 structure.add_bounds(AddBounds::Fields);
94
95 let crate_name = path.iter().next().unwrap();
96 let crate_name = quote!(#crate_name).to_string();
97
98 structure.gen_impl(quote! {
99 gen impl databake::Bake for @Self {
100 fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream {
101 env.insert(#crate_name);
102 match self {
103 #bake_body
104 &_ => unreachable!() }
106 }
107 }
108 gen impl databake::BakeSize for @Self {
109 fn borrows_size(&self) -> usize {
110 match self {
111 #borrows_size_body
112 &_ => unreachable!() }
114 }
115 }
116 })
117}