reth_codecs_derive/compact/
generator.rs1use super::*;
4use crate::ZstdConfig;
5use syn::{Attribute, LitStr};
6
7pub fn generate_from_to(
9 ident: &Ident,
10 attrs: &[Attribute],
11 has_lifetime: bool,
12 fields: &FieldList,
13 zstd: Option<ZstdConfig>,
14) -> TokenStream2 {
15 let flags = format_ident!("{ident}Flags");
16
17 let reth_codecs = parse_reth_codecs_path(attrs).unwrap();
18
19 let to_compact = generate_to_compact(fields, ident, zstd.clone(), &reth_codecs);
20 let from_compact = generate_from_compact(fields, ident, zstd);
21
22 let lifetime = if has_lifetime {
23 quote! { 'a }
24 } else {
25 quote! {}
26 };
27
28 let impl_compact = if has_lifetime {
29 quote! {
30 impl<#lifetime> #reth_codecs::Compact for #ident<#lifetime>
31 }
32 } else {
33 quote! {
34 impl #reth_codecs::Compact for #ident
35 }
36 };
37
38 let has_ref_fields = fields.iter().any(|field| {
39 if let FieldTypes::StructField(field) = field {
40 field.is_reference
41 } else {
42 false
43 }
44 });
45
46 let fn_from_compact = if has_ref_fields {
47 quote! { unimplemented!("from_compact not supported with ref structs") }
48 } else {
49 quote! {
50 let (flags, mut buf) = #flags::from(buf);
51 #from_compact
52 }
53 };
54
55 quote! {
57 #impl_compact {
58 fn to_compact<B>(&self, buf: &mut B) -> usize where B: #reth_codecs::__private::bytes::BufMut + AsMut<[u8]> {
59 let mut flags = #flags::default();
60 let mut total_length = 0;
61 #(#to_compact)*
62 total_length
63 }
64
65 fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) {
66 #fn_from_compact
67 }
68 }
69 }
70}
71
72fn generate_from_compact(
74 fields: &FieldList,
75 ident: &Ident,
76 zstd: Option<ZstdConfig>,
77) -> TokenStream2 {
78 let mut lines = vec![];
79 let mut known_types =
80 vec!["B256", "Address", "Bloom", "Vec", "TxHash", "BlockHash", "FixedBytes", "Cow"];
81
82 known_types.extend_from_slice(&["TxKind", "AccessList", "Signature", "CheckpointBlockRange"]);
87
88 let is_enum = fields.iter().any(|field| matches!(field, FieldTypes::EnumVariant(_)));
90
91 if is_enum {
92 let enum_lines = EnumHandler::new(fields).generate_from(ident);
93
94 lines.push(quote! {
96 let obj = match flags.variant() {
97 #(#enum_lines)*
98 _ => unreachable!()
99 };
100 });
101 } else {
102 let mut struct_handler = StructHandler::new(fields);
103 lines.append(&mut struct_handler.generate_from(known_types.as_slice()));
104
105 if struct_handler.is_wrapper {
107 lines.push(quote! {
108 let obj = #ident(placeholder);
109 });
110 } else {
111 let fields = fields.iter().filter_map(|field| {
112 if let FieldTypes::StructField(field) = field {
113 let ident = format_ident!("{}", field.name);
114 return Some(quote! {
115 #ident: #ident,
116 })
117 }
118 None
119 });
120
121 lines.push(quote! {
122 let obj = #ident {
123 #(#fields)*
124 };
125 });
126 }
127 }
128
129 if let Some(zstd) = zstd {
133 let decompressor = zstd.decompressor;
134 quote! {
135 if flags.__zstd() != 0 {
136 #decompressor(|decompressor| {
137 let decompressed = decompressor.decompress(buf);
138 let mut original_buf = buf;
139
140 let mut buf: &[u8] = decompressed;
141 #(#lines)*
142 (obj, original_buf)
143 })
144 } else {
145 #(#lines)*
146 (obj, buf)
147 }
148 }
149 } else {
150 quote! {
151 #(#lines)*
152 (obj, buf)
153 }
154 }
155}
156
157fn generate_to_compact(
159 fields: &FieldList,
160 ident: &Ident,
161 zstd: Option<ZstdConfig>,
162 reth_codecs: &syn::Path,
163) -> Vec<TokenStream2> {
164 let mut lines = vec![quote! {
165 let mut buffer = #reth_codecs::__private::bytes::BytesMut::new();
166 }];
167
168 let is_enum = fields.iter().any(|field| matches!(field, FieldTypes::EnumVariant(_)));
169
170 if is_enum {
171 let enum_lines = EnumHandler::new(fields).generate_to(ident);
172
173 lines.push(quote! {
174 flags.set_variant(match self {
175 #(#enum_lines)*
176 });
177 })
178 } else {
179 lines.append(&mut StructHandler::new(fields).generate_to());
180 }
181
182 if zstd.is_some() {
186 lines.push(quote! {
187 let mut zstd = buffer.len() > 7;
188 if zstd {
189 flags.set___zstd(1);
190 }
191 });
192 }
193
194 lines.push(quote! {
196 let flags = flags.into_bytes();
197 total_length += flags.len() + buffer.len();
198 buf.put_slice(&flags);
199 });
200
201 if let Some(zstd) = zstd {
202 let compressor = zstd.compressor;
203 lines.push(quote! {
204 if zstd {
205 #compressor(|compressor| {
206 let compressed = compressor.compress(&buffer).expect("Failed to compress.");
207 buf.put(compressed.as_slice());
208 });
209 } else {
210 buf.put(buffer);
211 }
212 });
213 } else {
214 lines.push(quote! {
215 buf.put(buffer);
216 })
217 }
218
219 lines
220}
221
222pub(crate) fn parse_reth_codecs_path(attrs: &[Attribute]) -> syn::Result<syn::Path> {
224 let mut reth_codecs_path: syn::Path = syn::parse_quote!(reth_codecs);
226 for attr in attrs {
227 if attr.path().is_ident("reth_codecs") {
228 attr.parse_nested_meta(|meta| {
229 if meta.path.is_ident("crate") {
230 let value = meta.value()?;
231 let lit: LitStr = value.parse()?;
232 reth_codecs_path = syn::parse_str(&lit.value())?;
233 Ok(())
234 } else {
235 Err(meta.error("unsupported attribute"))
236 }
237 })?;
238 }
239 }
240
241 Ok(reth_codecs_path)
242}