reth_codecs_derive/compact/
structs.rs

1use super::*;
2
3#[derive(Debug)]
4pub struct StructHandler<'a> {
5    fields_iterator: std::iter::Peekable<std::slice::Iter<'a, FieldTypes>>,
6    lines: Vec<TokenStream2>,
7    pub is_wrapper: bool,
8}
9
10impl<'a> StructHandler<'a> {
11    pub fn new(fields: &'a FieldList) -> Self {
12        StructHandler {
13            lines: vec![],
14            fields_iterator: fields.iter().peekable(),
15            is_wrapper: false,
16        }
17    }
18
19    pub fn next_field(&mut self) -> Option<&'a FieldTypes> {
20        self.fields_iterator.next()
21    }
22
23    pub fn generate_to(mut self) -> Vec<TokenStream2> {
24        while let Some(field) = self.next_field() {
25            match field {
26                FieldTypes::EnumVariant(_) | FieldTypes::EnumUnnamedField(_) => unreachable!(),
27                FieldTypes::StructField(field_descriptor) => self.to(field_descriptor),
28            }
29        }
30        self.lines
31    }
32
33    pub fn generate_from(&mut self, known_types: &[&str]) -> Vec<TokenStream2> {
34        while let Some(field) = self.next_field() {
35            match field {
36                FieldTypes::EnumVariant(_) | FieldTypes::EnumUnnamedField(_) => unreachable!(),
37                FieldTypes::StructField(field_descriptor) => {
38                    self.from(field_descriptor, known_types)
39                }
40            }
41        }
42        self.lines.clone()
43    }
44
45    /// Generates `to_compact` code for a struct field.
46    fn to(&mut self, field_descriptor: &StructFieldDescriptor) {
47        let StructFieldDescriptor { name, ftype, is_compact, use_alt_impl, is_reference: _ } =
48            field_descriptor;
49
50        let to_compact_ident = if *use_alt_impl {
51            format_ident!("specialized_to_compact")
52        } else {
53            format_ident!("to_compact")
54        };
55
56        // Should only happen on wrapper structs like `Struct(pub Field)`
57        if name.is_empty() {
58            self.is_wrapper = true;
59
60            self.lines.push(quote! {
61                let _len = self.0.#to_compact_ident(&mut buffer);
62            });
63
64            if is_flag_type(ftype) {
65                self.lines.push(quote! {
66                    flags.set_placeholder_len(_len as u8);
67                })
68            }
69
70            return
71        }
72
73        let name = format_ident!("{name}");
74        let set_len_method = format_ident!("set_{name}_len");
75        let len = format_ident!("{name}_len");
76
77        // B256 with #[maybe_zero] attribute for example
78        if *is_compact && !is_flag_type(ftype) {
79            let itype = format_ident!("{ftype}");
80            let set_bool_method = format_ident!("set_{name}");
81            self.lines.push(quote! {
82                if self.#name != #itype::zero() {
83                    flags.#set_bool_method(true);
84                    self.#name.#to_compact_ident(&mut buffer);
85                };
86            });
87        } else {
88            self.lines.push(quote! {
89                let #len = self.#name.#to_compact_ident(&mut buffer);
90            });
91        }
92        if is_flag_type(ftype) {
93            self.lines.push(quote! {
94                flags.#set_len_method(#len as u8);
95            })
96        }
97    }
98
99    /// Generates `from_compact` code for a struct field.
100    fn from(&mut self, field_descriptor: &StructFieldDescriptor, known_types: &[&str]) {
101        let StructFieldDescriptor { name, ftype, is_compact, use_alt_impl, .. } = field_descriptor;
102
103        let (name, len) = if name.is_empty() {
104            self.is_wrapper = true;
105
106            // Should only happen on wrapper structs like `Struct(pub Field)`
107            (format_ident!("placeholder"), format_ident!("placeholder_len"))
108        } else {
109            (format_ident!("{name}"), format_ident!("{name}_len"))
110        };
111
112        let from_compact_ident = if *use_alt_impl {
113            format_ident!("specialized_from_compact")
114        } else {
115            format_ident!("from_compact")
116        };
117
118        // ! Be careful before changing the following assert ! Especially if the type does not
119        // implement proptest tests.
120        //
121        // The limitation of the last placed field applies to fields with potentially large sizes,
122        // like the `Transaction` field. These fields may have inner "Bytes" fields, sometimes even
123        // nested further, making it impossible to check with `proc_macro`. The goal is to place
124        // such fields as the last ones, so we don't need to store their length separately. Instead,
125        // we can simply read them until the end of the buffer.
126        //
127        // However, certain types don't require this approach because they don't contain inner
128        // "Bytes" fields. For these types, we can add them to a "known_types" list so it doesn't
129        // trigger this error. These types can handle their own deserialization without
130        // relying on the length provided by the higher-level deserializer. For example, a
131        // type "T" with two "u64" fields doesn't need the length parameter from
132        // "T::from_compact(buf, len)" since the length of "u64" is known internally (bitpacked).
133        assert!(
134            known_types.contains(&ftype.as_str()) ||
135                is_flag_type(ftype) ||
136                self.fields_iterator.peek().is_none(),
137            "`{ftype}` field should be placed as the last one since it's not known.
138            If it's an alias type (which are not supported by proc_macro), be sure to add it to either `known_types` or `get_bit_size` lists in the derive crate."
139        );
140
141        if ftype == "Bytes" {
142            self.lines.push(quote! {
143                let mut #name = Bytes::new();
144                (#name, buf) = Bytes::from_compact(buf, buf.len() as usize);
145            })
146        } else {
147            let ident_type = format_ident!("{ftype}");
148            if !is_flag_type(ftype) {
149                // It's a type that handles its own length requirements. (B256, Custom, ...)
150                self.lines.push(quote! {
151                    let (#name, new_buf) = #ident_type::#from_compact_ident(buf, buf.len());
152                })
153            } else if *is_compact {
154                self.lines.push(quote! {
155                    let (#name, new_buf) = #ident_type::#from_compact_ident(buf, flags.#len() as usize);
156                });
157            } else {
158                todo!()
159            }
160            self.lines.push(quote! {
161                buf = new_buf;
162            });
163        }
164    }
165}