1use super::*;
23#[derive(Debug)]
4pub struct StructHandler<'a> {
5 fields_iterator: std::iter::Peekable<std::slice::Iter<'a, FieldTypes>>,
6 lines: Vec<TokenStream2>,
7pub is_wrapper: bool,
8}
910impl<'a> StructHandler<'a> {
11pub fn new(fields: &'a FieldList) -> Self {
12StructHandler {
13 lines: vec![],
14 fields_iterator: fields.iter().peekable(),
15 is_wrapper: false,
16 }
17 }
1819pub fn next_field(&mut self) -> Option<&'a FieldTypes> {
20self.fields_iterator.next()
21 }
2223pub fn generate_to(mut self) -> Vec<TokenStream2> {
24while let Some(field) = self.next_field() {
25match field {
26 FieldTypes::EnumVariant(_) | FieldTypes::EnumUnnamedField(_) => unreachable!(),
27 FieldTypes::StructField(field_descriptor) => self.to(field_descriptor),
28 }
29 }
30self.lines
31 }
3233pub fn generate_from(&mut self, known_types: &[&str]) -> Vec<TokenStream2> {
34while let Some(field) = self.next_field() {
35match field {
36 FieldTypes::EnumVariant(_) | FieldTypes::EnumUnnamedField(_) => unreachable!(),
37 FieldTypes::StructField(field_descriptor) => {
38self.from(field_descriptor, known_types)
39 }
40 }
41 }
42self.lines.clone()
43 }
4445/// Generates `to_compact` code for a struct field.
46fn to(&mut self, field_descriptor: &StructFieldDescriptor) {
47let StructFieldDescriptor { name, ftype, is_compact, use_alt_impl, is_reference: _ } =
48field_descriptor;
4950let to_compact_ident = if *use_alt_impl {
51format_ident!("specialized_to_compact")
52 } else {
53format_ident!("to_compact")
54 };
5556// Should only happen on wrapper structs like `Struct(pub Field)`
57if name.is_empty() {
58self.is_wrapper = true;
5960self.lines.push(quote! {
61let _len = self.0.#to_compact_ident(&mut buffer);
62 });
6364if is_flag_type(ftype) {
65self.lines.push(quote! {
66 flags.set_placeholder_len(_len as u8);
67 })
68 }
6970return
71}
7273let name = format_ident!("{name}");
74let set_len_method = format_ident!("set_{name}_len");
75let len = format_ident!("{name}_len");
7677// B256 with #[maybe_zero] attribute for example
78if *is_compact && !is_flag_type(ftype) {
79let itype = format_ident!("{ftype}");
80let set_bool_method = format_ident!("set_{name}");
81self.lines.push(quote! {
82if self.#name != #itype::zero() {
83 flags.#set_bool_method(true);
84self.#name.#to_compact_ident(&mut buffer);
85 };
86 });
87 } else {
88self.lines.push(quote! {
89let #len = self.#name.#to_compact_ident(&mut buffer);
90 });
91 }
92if is_flag_type(ftype) {
93self.lines.push(quote! {
94 flags.#set_len_method(#len as u8);
95 })
96 }
97 }
9899/// Generates `from_compact` code for a struct field.
100fn from(&mut self, field_descriptor: &StructFieldDescriptor, known_types: &[&str]) {
101let StructFieldDescriptor { name, ftype, is_compact, use_alt_impl, .. } = field_descriptor;
102103let (name, len) = if name.is_empty() {
104self.is_wrapper = true;
105106// 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 };
111112let from_compact_ident = if *use_alt_impl {
113format_ident!("specialized_from_compact")
114 } else {
115format_ident!("from_compact")
116 };
117118// ! 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).
133assert!(
134 known_types.contains(&ftype.as_str()) ||
135 is_flag_type(ftype) ||
136self.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);
140141if ftype == "Bytes" {
142self.lines.push(quote! {
143let mut #name = Bytes::new();
144 (#name, buf) = Bytes::from_compact(buf, buf.len() as usize);
145 })
146 } else {
147let ident_type = format_ident!("{ftype}");
148if !is_flag_type(ftype) {
149// It's a type that handles its own length requirements. (B256, Custom, ...)
150self.lines.push(quote! {
151let (#name, new_buf) = #ident_type::#from_compact_ident(buf, buf.len());
152 })
153 } else if *is_compact {
154self.lines.push(quote! {
155let (#name, new_buf) = #ident_type::#from_compact_ident(buf, flags.#len() as usize);
156 });
157 } else {
158todo!()
159 }
160self.lines.push(quote! {
161 buf = new_buf;
162 });
163 }
164 }
165}