neli_proc_macros/
derive_size.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    FieldInfo, StructInfo,
8};
9
10fn generate_size(i: StructInfo) -> TokenStream2 {
11    let (struct_name, generics, generics_without_bounds, field_names, field_types, _, _) =
12        i.into_tuple();
13
14    if field_types.is_empty() {
15        quote! {
16            impl#generics neli::Size for #struct_name#generics_without_bounds {
17                fn unpadded_size(&self) -> usize {
18                    0
19                }
20            }
21        }
22    } else {
23        quote! {
24            impl#generics neli::Size for #struct_name#generics_without_bounds {
25                fn unpadded_size(&self) -> usize {
26                    #( <#field_types as neli::Size>::unpadded_size(&self.#field_names) )+*
27                }
28            }
29        }
30    }
31}
32
33pub fn impl_size_struct(is: ItemStruct) -> TokenStream2 {
34    let struct_info = StructInfo::from_item_struct(is, Some("Size"), "size_bound", true);
35    generate_size(struct_info)
36}
37
38fn generate_named_pat_and_expr(
39    enum_name: Ident,
40    var_name: Ident,
41    fields: FieldsNamed,
42) -> TokenStream2 {
43    let (field_names, types, _) = FieldInfo::to_vecs(generate_named_fields(fields).into_iter());
44    quote! {
45        #enum_name::#var_name {
46            #(#field_names),*
47        } => {
48            #(<#types as neli::Size>::unpadded_size(&#field_names))+*
49        },
50    }
51}
52
53fn generate_unnamed_pat_and_expr(
54    enum_name: Ident,
55    var_name: Ident,
56    fields: FieldsUnnamed,
57) -> TokenStream2 {
58    let (field_names, types, _) =
59        FieldInfo::to_vecs(generate_unnamed_fields(fields, false).into_iter());
60    quote! {
61        #enum_name::#var_name(
62            #( #field_names ),*
63        ) => {
64            #( <#types as neli::Size>::unpadded_size(&#field_names) )+*
65        }
66    }
67}
68
69pub fn impl_size_enum(ie: ItemEnum) -> TokenStream2 {
70    let (generics, generics_without_bounds) = process_impl_generics(ie.generics, Some("Size"));
71
72    let enum_name = ie.ident;
73    let arms = generate_arms(
74        enum_name.clone(),
75        ie.variants.into_iter().collect::<Vec<_>>(),
76        generate_named_pat_and_expr,
77        generate_unnamed_pat_and_expr,
78        quote! {
79            0
80        },
81    );
82    quote! {
83        impl#generics neli::Size for #enum_name#generics_without_bounds {
84            fn unpadded_size(&self) -> usize {
85                match self {
86                    #(#arms)*
87                }
88            }
89        }
90    }
91}