neli_proc_macros/
derive_tobytes.rs

1use proc_macro2::TokenStream as TokenStream2;
2use quote::quote;
3use syn::{FieldsNamed, FieldsUnnamed, Ident, ItemEnum, ItemStruct};
4
5use crate::shared::{
6    generate_arms, generate_named_fields, generate_unnamed_fields, process_impl_generics,
7    process_trait_bounds, FieldInfo, StructInfo,
8};
9
10pub fn impl_tobytes_struct(is: ItemStruct) -> TokenStream2 {
11    let info = StructInfo::from_item_struct(is, Some("ToBytes"), "to_bytes_bound", true);
12    let (struct_name, generics, generics_without_bounds, field_names, field_types, _, padded) =
13        info.into_tuple();
14
15    if field_names.is_empty() {
16        return quote! {
17            impl neli::ToBytes for #struct_name {
18                fn to_bytes(&self, _: &mut std::io::Cursor<Vec<u8>>) -> Result<(), neli::err::SerError> {
19                    Ok(())
20                }
21            }
22        };
23    }
24
25    let padding = if padded {
26        quote! {
27            <#struct_name#generics_without_bounds as neli::ToBytes>::pad(&self, buffer)?;
28        }
29    } else {
30        TokenStream2::new()
31    };
32
33    quote! {
34        impl#generics neli::ToBytes for #struct_name#generics_without_bounds {
35            fn to_bytes(&self, buffer: &mut std::io::Cursor<Vec<u8>>) -> Result<(), neli::err::SerError> {
36                #( <#field_types as neli::ToBytes>::to_bytes(&self.#field_names, buffer)?; )*
37                #padding
38                Ok(())
39            }
40        }
41    }
42}
43
44fn generate_named_pat_and_expr(
45    enum_name: Ident,
46    var_name: Ident,
47    fields: FieldsNamed,
48) -> TokenStream2 {
49    let (field_names, types, _) = FieldInfo::to_vecs(generate_named_fields(fields).into_iter());
50    quote! {
51        #enum_name::#var_name {
52            #(#field_names),*
53        } => {
54            #(<#types as neli::ToBytes>::to_bytes(&#field_names, buffer)?; )*
55            Ok(())
56        },
57    }
58}
59
60fn generate_unnamed_pat_and_expr(
61    enum_name: Ident,
62    var_name: Ident,
63    fields: FieldsUnnamed,
64) -> TokenStream2 {
65    let (field_names, types, _) =
66        FieldInfo::to_vecs(generate_unnamed_fields(fields, false).into_iter());
67    quote! {
68        #enum_name::#var_name(
69            #( #field_names ),*
70        ) => {
71            #( <#types as neli::ToBytes>::to_bytes(#field_names, buffer)?; )*
72            Ok(())
73        }
74    }
75}
76
77pub fn impl_tobytes_enum(ie: ItemEnum) -> TokenStream2 {
78    let (generics, generics_without_bounds) = process_impl_generics(ie.generics, Some("ToBytes"));
79    let trait_bounds = process_trait_bounds(&ie.attrs, "to_bytes_bound");
80
81    let enum_name = ie.ident;
82    let arms = generate_arms(
83        enum_name.clone(),
84        ie.variants.into_iter().collect::<Vec<_>>(),
85        generate_named_pat_and_expr,
86        generate_unnamed_pat_and_expr,
87        quote! {
88            Ok(())
89        },
90    );
91    quote! {
92        impl#generics neli::ToBytes for #enum_name#generics_without_bounds where #( #trait_bounds ),* {
93            fn to_bytes(&self, buffer: &mut std::io::Cursor<Vec<u8>>) -> Result<(), neli::err::SerError> {
94                match self {
95                    #(#arms)*
96                }
97            }
98        }
99    }
100}