1#![warn(missing_docs)]
17
18use proc_macro::TokenStream;
21use proc_macro2::TokenStream as TokenStream2;
22use quote::quote;
23use syn::{
24 parse::{Parse, ParseStream},
25 parse_macro_input,
26 punctuated::Punctuated,
27 Data, DeriveInput, Ident, Path, PathSegment, Token, Visibility,
28};
29use synstructure::{AddBounds, Structure};
30
31#[proc_macro_derive(Bake, attributes(databake))]
48pub fn bake_derive(input: TokenStream) -> TokenStream {
49 let input = parse_macro_input!(input as DeriveInput);
50 TokenStream::from(bake_derive_impl(&input))
51}
52
53fn bake_derive_impl(input: &DeriveInput) -> TokenStream2 {
54 let mut structure = Structure::new(input);
55
56 struct PathAttr(Punctuated<PathSegment, Token![::]>);
57
58 impl Parse for PathAttr {
59 fn parse(input: ParseStream<'_>) -> syn::parse::Result<Self> {
60 let i: Ident = input.parse()?;
61 if i != "path" {
62 return Err(input.error(format!("expected token \"path\", found {i:?}")));
63 }
64 input.parse::<Token![=]>()?;
65 Ok(Self(input.parse::<Path>()?.segments))
66 }
67 }
68
69 let path = input
70 .attrs
71 .iter()
72 .find(|a| a.path().is_ident("databake"))
73 .expect("missing databake(path = ...) attribute")
74 .parse_args::<PathAttr>()
75 .unwrap()
76 .0;
77
78 let bake_body = structure.each_variant(|vi| {
79 let recursive_calls = vi.bindings().iter().map(|b| {
80 let ident = b.binding.clone();
81 quote! { let #ident = #ident.bake(env); }
82 });
83
84 let constructor = vi.construct(|f, i| {
85 assert!(
86 f.vis == syn::parse_str::<Visibility>("pub").unwrap()
87 || matches!(input.data, Data::Enum(_)),
88 "deriving Bake requires public fields"
89 );
90 let ident = &vi.bindings()[i].binding;
91 quote! { # #ident }
92 });
93
94 quote! {
95 #(#recursive_calls)*
96 databake::quote! { #path::#constructor }
97 }
98 });
99
100 let borrows_size_body = structure.each_variant(|vi| {
101 let recursive_calls = vi.bindings().iter().map(|b| {
102 let ident = b.binding.clone();
103 quote! { #ident.borrows_size() }
104 });
105
106 quote! {
107 0 #(+ #recursive_calls)*
108 }
109 });
110
111 structure.add_bounds(AddBounds::Fields);
112
113 let crate_name = path.iter().next().unwrap();
114 let crate_name = quote!(#crate_name).to_string();
115
116 structure.gen_impl(quote! {
117 gen impl databake::Bake for @Self {
118 fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream {
119 env.insert(#crate_name);
120 match self {
121 #bake_body
122 &_ => unreachable!() }
124 }
125 }
126 gen impl databake::BakeSize for @Self {
127 fn borrows_size(&self) -> usize {
128 match self {
129 #borrows_size_body
130 &_ => unreachable!() }
132 }
133 }
134 })
135}