Skip to main content

bincode_trait_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4    Data, DeriveInput, Fields, GenericParam, Ident, Lifetime, LifetimeParam, Path, PathArguments,
5    Type, TypeParam, TypePath, WherePredicate, parse_macro_input, spanned::Spanned,
6};
7
8#[proc_macro_derive(Encode)]
9pub fn encode_derive(input: TokenStream) -> TokenStream {
10    let input = parse_macro_input!(input as DeriveInput);
11    let struct_name = input.ident;
12
13    let mut generics_for_impl = input.generics.clone();
14    let mut where_clause_for_impl = generics_for_impl.make_where_clause().clone();
15
16    // Add bounds for generic type parameters
17    for param in input.generics.params.iter() {
18        if let GenericParam::Type(type_param) = param {
19            let type_ident = &type_param.ident;
20            let type_path = Type::Path(TypePath {
21                qself: None,
22                path: type_ident.clone().into(),
23            });
24            let predicate: WherePredicate = syn::parse_quote! {
25                #type_path: ::bincode::Encode
26            };
27            where_clause_for_impl.predicates.push(predicate);
28        }
29    }
30
31    // Extract field types to add additional bounds for associated types
32    let field_types = match &input.data {
33        Data::Struct(data_struct) => data_struct.fields.iter().map(|f| &f.ty).collect::<Vec<_>>(),
34        Data::Enum(data_enum) => data_enum
35            .variants
36            .iter()
37            .flat_map(|variant| variant.fields.iter().map(|f| &f.ty))
38            .collect::<Vec<_>>(),
39        Data::Union(_) => vec![],
40    };
41
42    // Check for associated types in field types and add bounds for them
43    for ty in field_types {
44        if let Type::Path(type_path) = ty {
45            // Check for direct associated types like F::Element
46            if type_path.path.segments.len() > 1 {
47                let predicate: WherePredicate = syn::parse_quote! {
48                    #ty: ::bincode::Encode
49                };
50                where_clause_for_impl.predicates.push(predicate);
51            }
52
53            // Look for associated types inside generic containers like Vec<F::Element>
54            if !type_path.path.segments.is_empty() {
55                for segment in &type_path.path.segments {
56                    if let PathArguments::AngleBracketed(args) = &segment.arguments {
57                        for arg in &args.args {
58                            if let syn::GenericArgument::Type(Type::Path(inner_type_path)) = arg {
59                                // Check if this is an associated type (contains ::)
60                                if inner_type_path.path.segments.len() > 1 {
61                                    let inner_type = Type::Path(inner_type_path.clone());
62                                    let predicate: WherePredicate = syn::parse_quote! {
63                                        #inner_type: ::bincode::Encode
64                                    };
65                                    where_clause_for_impl.predicates.push(predicate);
66                                }
67                            }
68                        }
69                    }
70                }
71            }
72        }
73    }
74
75    let (impl_generics, _, _) = generics_for_impl.split_for_impl();
76    let (_, ty_generics, _) = input.generics.split_for_impl();
77
78    let encode_body = match &input.data {
79        Data::Struct(data_struct) => match &data_struct.fields {
80            Fields::Named(fields_named) => {
81                let encode_fields = fields_named.named.iter().map(|f| {
82                    let ident = &f.ident;
83                    quote! { ::bincode::Encode::encode(&self.#ident, encoder)?; }
84                });
85                quote! { #(#encode_fields)* Ok(()) }
86            }
87            Fields::Unnamed(fields_unnamed) => {
88                let encode_fields = fields_unnamed.unnamed.iter().enumerate().map(|(i, _)| {
89                    let index = syn::Index::from(i);
90                    quote! { ::bincode::Encode::encode(&self.#index, encoder)?; }
91                });
92                quote! { #(#encode_fields)* Ok(()) }
93            }
94            Fields::Unit => quote! { Ok(()) },
95        },
96        Data::Enum(data_enum) => {
97            let variant_arms = data_enum.variants.iter().enumerate().map(|(idx, variant)| {
98                let variant_ident = &variant.ident;
99                let discriminant = proc_macro2::Literal::usize_suffixed(idx);
100                match &variant.fields {
101                    Fields::Named(fields_named) => {
102                        let field_pats = fields_named.named.iter().map(|f| &f.ident);
103                        let field_encodes = fields_named.named.iter().map(|f| {
104                            let ident = &f.ident;
105                            quote! { ::bincode::Encode::encode(#ident, encoder)?; }
106                        });
107                        quote! {
108                            Self::#variant_ident { #(#field_pats),* } => {
109                                ::bincode::Encode::encode(&#discriminant, encoder)?;
110                                #(#field_encodes)*
111                                Ok(())
112                            }
113                        }
114                    }
115                    Fields::Unnamed(fields_unnamed) => {
116                        let field_pats_bindings = fields_unnamed
117                            .unnamed
118                            .iter()
119                            .enumerate()
120                            .map(|(i, field)| Ident::new(&format!("field{}", i), field.span()));
121                        let field_pats = field_pats_bindings.clone();
122                        let field_encodes = field_pats_bindings.map(|binding| {
123                            quote! { ::bincode::Encode::encode(#binding, encoder)?; }
124                        });
125                        quote! {
126                            Self::#variant_ident ( #(#field_pats),* ) => {
127                                ::bincode::Encode::encode(&#discriminant, encoder)?;
128                                #(#field_encodes)*
129                                Ok(())
130                            }
131                        }
132                    }
133                    Fields::Unit => {
134                        quote! {
135                            Self::#variant_ident => {
136                                ::bincode::Encode::encode(&#discriminant, encoder)?;
137                                Ok(())
138                            }
139                        }
140                    }
141                }
142            });
143            quote! {
144                match self {
145                    #(#variant_arms)*
146                }
147            }
148        }
149        Data::Union(_) => unimplemented!("Unions are not supported by Encode derive"),
150    };
151
152    let expanded = quote! {
153        impl #impl_generics ::bincode::Encode for #struct_name #ty_generics #where_clause_for_impl {
154            fn encode<__E: ::bincode::enc::Encoder>(&self, encoder: &mut __E) -> std::result::Result<(), ::bincode::error::EncodeError> {
155                #encode_body
156            }
157        }
158    };
159
160    TokenStream::from(expanded)
161}
162
163#[proc_macro_derive(Decode, attributes(trait_decode))]
164pub fn trait_derive(input: TokenStream) -> TokenStream {
165    let mut input_ast = parse_macro_input!(input as DeriveInput);
166    let struct_name = &input_ast.ident;
167
168    let mut option_trait_name: Option<Path> = None;
169    let mut option_context_type_name: Option<Path> = None;
170
171    for attr in input_ast
172        .attrs
173        .iter()
174        .filter(|a| a.path().is_ident("trait_decode"))
175    {
176        if let Err(e) = attr.parse_nested_meta(|meta| {
177            if meta.path.is_ident("trait") {
178                if option_context_type_name.is_some() {
179                    return Err(meta.error("cannot specify both `trait` and `context_type` in #[trait_decode]"));
180                }
181                option_trait_name = Some(meta.value()?.parse::<Path>()?);
182                Ok(())
183            } else if meta.path.is_ident("context_type") {
184                if option_trait_name.is_some() {
185                    return Err(meta.error("cannot specify both `trait` and `context_type` in #[trait_decode]"));
186                }
187                option_context_type_name = Some(meta.value()?.parse::<Path>()?);
188                Ok(())
189            } else {
190                Err(meta.error("unrecognized key for #[trait_decode] attribute, supported keys are `trait` and `context_type`"))
191            }
192        }) {
193            return e.to_compile_error().into();
194        }
195    }
196
197    let mut generics_for_impl = input_ast.generics.clone();
198    let mut where_clause_for_impl = input_ast.generics.make_where_clause().clone();
199
200    // Only add a generic parameter if we don't have a concrete context type
201    let context_generic_ident = Ident::new("__Context", proc_macro2::Span::call_site());
202
203    // Only add the generic parameter if not using a concrete context type
204    if option_context_type_name.is_none() {
205        let context_generic_param_for_impl =
206            GenericParam::Type(TypeParam::from(context_generic_ident.clone()));
207
208        generics_for_impl
209            .params
210            .push(context_generic_param_for_impl);
211    }
212
213    if let Some(ref trait_ident_path) = option_trait_name {
214        let pred: WherePredicate = syn::parse_quote! { #context_generic_ident: #trait_ident_path };
215        where_clause_for_impl.predicates.push(pred);
216    } else if let Some(ref _concrete_type_path) = option_context_type_name {
217        // Instead of using the context_type directly in the where clause, we'll use it in the impl
218        // Replace the generic context parameter with the concrete type
219        // Just don't add any where predicates for the context
220    } else {
221        // No attribute specifying trait or context_type. __Context remains generic for this impl.
222        // It will be constrained by field requirements, e.g., `usize: Decode<__Context>` implies `__Context = ()`.
223    }
224
225    // Add `TypeParameter: Decode<__Context>` bounds for the struct's own type parameters.
226    for param in input_ast.generics.params.iter() {
227        if let GenericParam::Type(type_param) = param {
228            let type_ident = &type_param.ident;
229            let type_path = Type::Path(TypePath {
230                qself: None,
231                path: type_ident.clone().into(),
232            });
233            let predicate: WherePredicate = syn::parse_quote! {
234                #type_path: ::bincode::Decode<#context_generic_ident>
235            };
236            where_clause_for_impl.predicates.push(predicate);
237        }
238    }
239
240    // Create the context type based on whether it's generic or concrete
241    let context_type = if let Some(ref concrete_type_path) = option_context_type_name {
242        // If we're using a concrete type, use it directly
243        quote! { #concrete_type_path }
244    } else {
245        // Otherwise use the generic parameter
246        quote! { #context_generic_ident }
247    };
248
249    let (impl_generics, _, _) = generics_for_impl.split_for_impl(); // Contains original generics + __Context
250    let (_, ty_generics_for_struct, _) = input_ast.generics.split_for_impl(); // Original generics for struct type
251
252    let decode_body = match &input_ast.data {
253        Data::Struct(data_struct) => match &data_struct.fields {
254            Fields::Named(fields_named) => {
255                let decode_fields = fields_named.named.iter().map(|f| {
256                    let ident = &f.ident;
257                    quote! { #ident: ::bincode::Decode::decode(decoder)? }
258                });
259                quote! { Ok(Self { #(#decode_fields),* }) }
260            }
261            Fields::Unnamed(fields_unnamed) => {
262                let decode_fields = fields_unnamed.unnamed.iter().map(|_| {
263                    quote! { ::bincode::Decode::decode(decoder)? }
264                });
265                quote! { Ok(Self(#(#decode_fields),*)) }
266            }
267            Fields::Unit => quote! { Ok(Self) },
268        },
269        Data::Enum(data_enum) => {
270            let num_variants = data_enum.variants.iter().count();
271
272            let variants = data_enum.variants.iter().enumerate().map(|(idx, variant)| {
273                let variant_ident = &variant.ident;
274                match &variant.fields {
275                    Fields::Named(fields_named) => {
276                        let decode_fields = fields_named.named.iter().map(|f| {
277                            let ident = &f.ident;
278                            quote! { #ident: ::bincode::Decode::decode(decoder)? }
279                        });
280                        quote! { #idx => Ok(Self::#variant_ident { #(#decode_fields),* }), }
281                    }
282                    Fields::Unnamed(fields_unnamed) => {
283                        let decode_fields = fields_unnamed.unnamed.iter().map(|_| {
284                            quote! { ::bincode::Decode::decode(decoder)? }
285                        });
286                        quote! { #idx => Ok(Self::#variant_ident(#(#decode_fields),*)), }
287                    }
288                    Fields::Unit => quote! { #idx => Ok(Self::#variant_ident), },
289                }
290            });
291            quote! {
292                let discriminant: usize = ::bincode::Decode::decode(decoder)?;
293                match discriminant {
294                    #(#variants)*
295                    _other => Err(::bincode::error::DecodeError::UnexpectedVariant {
296                        type_name: stringify!(#struct_name),
297                        found: _other as u32,
298                        allowed: &bincode::error::AllowedEnumVariants::Range {
299                            min: 0u32,
300                            max: (#num_variants - 1) as u32,
301                        },
302                    }),
303                }
304            }
305        }
306        Data::Union(_) => unimplemented!("Unions are not supported by Decode derive"),
307    };
308
309    let expanded = quote! {
310        impl #impl_generics ::bincode::Decode<#context_type> for #struct_name #ty_generics_for_struct #where_clause_for_impl {
311            fn decode<D: ::bincode::de::Decoder<Context = #context_type>>(decoder: &mut D) -> std::result::Result<Self, ::bincode::error::DecodeError> {
312                #decode_body
313            }
314        }
315    };
316
317    TokenStream::from(expanded)
318}
319
320#[proc_macro_derive(BorrowDecodeFromDecode, attributes(trait_decode))]
321pub fn borrow_decode_from_trait_decode(input: TokenStream) -> TokenStream {
322    let mut input_ast = parse_macro_input!(input as DeriveInput);
323    let struct_name = &input_ast.ident;
324
325    let mut option_trait_name: Option<Path> = None;
326    let mut option_context_type_name: Option<Path> = None;
327
328    for attr in input_ast
329        .attrs
330        .iter()
331        .filter(|a| a.path().is_ident("trait_decode"))
332    {
333        if let Err(e) = attr.parse_nested_meta(|meta| {
334            if meta.path.is_ident("trait") {
335                if option_context_type_name.is_some() {
336                    return Err(meta.error("cannot specify both `trait` and `context_type` in #[trait_decode]"));
337                }
338                option_trait_name = Some(meta.value()?.parse::<Path>()?);
339                Ok(())
340            } else if meta.path.is_ident("context_type") {
341                if option_trait_name.is_some() {
342                    return Err(meta.error("cannot specify both `trait` and `context_type` in #[trait_decode]"));
343                }
344                option_context_type_name = Some(meta.value()?.parse::<Path>()?);
345                Ok(())
346            } else {
347                Err(meta.error("unrecognized key for #[trait_decode] attribute, supported keys are `trait` and `context_type`"))
348            }
349        }) {
350            return e.to_compile_error().into();
351        }
352    }
353
354    let mut generics_for_impl = input_ast.generics.clone();
355    let mut where_clause_for_impl = input_ast.generics.make_where_clause().clone();
356
357    let lifetime_de_ident = Lifetime::new("'_de", proc_macro2::Span::call_site());
358    let lifetime_de_param = GenericParam::Lifetime(LifetimeParam::new(lifetime_de_ident.clone()));
359    generics_for_impl.params.push(lifetime_de_param);
360
361    // Only add a generic parameter if we don't have a concrete context type
362    let context_ident = Ident::new("__Context", proc_macro2::Span::call_site());
363
364    // Only add the generic parameter if not using a concrete context type
365    if option_context_type_name.is_none() {
366        let context_generic_param_for_impl =
367            GenericParam::Type(TypeParam::from(context_ident.clone()));
368        generics_for_impl
369            .params
370            .push(context_generic_param_for_impl);
371    }
372
373    if let Some(ref trait_ident_path) = option_trait_name {
374        let pred: WherePredicate = syn::parse_quote! { #context_ident: #trait_ident_path };
375        where_clause_for_impl.predicates.push(pred);
376    } else if let Some(ref _concrete_type_path) = option_context_type_name {
377        // Instead of using the context_type directly in the where clause, we'll use it in the impl
378        // Replace the generic context parameter with the concrete type
379        // Just don't add any where predicates for the context
380    } else {
381        // __Context remains generic for this impl.
382    }
383
384    // Add `TypeParameter: Decode<__Context>` bounds for the struct's own type parameters,
385    // as BorrowDecode calls Decode.
386    for param in input_ast.generics.params.iter() {
387        if let GenericParam::Type(type_param) = param {
388            let type_ident = &type_param.ident;
389            let type_path = Type::Path(TypePath {
390                qself: None,
391                path: type_ident.clone().into(),
392            });
393            let predicate: WherePredicate = syn::parse_quote! {
394                #type_path: ::bincode::Decode<#context_ident>
395            };
396            where_clause_for_impl.predicates.push(predicate);
397        }
398    }
399
400    // This struct itself must implement Decode<__Context> for BorrowDecode to call it.
401    // This should be implicitly handled if the Decode derive is also present and correct.
402    // If Decode is not derived, this might lead to issues, but that's outside this macro's scope.
403
404    // Create the context type based on whether it's generic or concrete
405    let context_type = if let Some(ref concrete_type_path) = option_context_type_name {
406        // If we're using a concrete type, use it directly
407        quote! { #concrete_type_path }
408    } else {
409        // Otherwise use the generic parameter
410        quote! { #context_ident }
411    };
412
413    let (impl_generics, _, _) = generics_for_impl.split_for_impl(); // Contains original generics + '_de + __Context
414    let (_, ty_generics_for_struct, _) = input_ast.generics.split_for_impl(); // Original generics for struct type
415
416    let expanded = quote! {
417        impl #impl_generics ::bincode::BorrowDecode<#lifetime_de_ident, #context_type> for #struct_name #ty_generics_for_struct #where_clause_for_impl {
418            fn borrow_decode<D: ::bincode::de::BorrowDecoder<#lifetime_de_ident, Context = #context_type>>(decoder: &mut D) -> std::result::Result<Self, ::bincode::error::DecodeError> {
419                <Self as ::bincode::Decode<#context_type>>::decode(decoder)
420            }
421        }
422    };
423
424    TokenStream::from(expanded)
425}