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.with(|decompressor| {
137 let decompressor = &mut decompressor.borrow_mut();
138 let decompressed = decompressor.decompress(buf);
139 let mut original_buf = buf;
140
141 let mut buf: &[u8] = decompressed;
142 #(#lines)*
143 (obj, original_buf)
144 })
145 } else {
146 #(#lines)*
147 (obj, buf)
148 }
149 }
150 } else {
151 quote! {
152 #(#lines)*
153 (obj, buf)
154 }
155 }
156}
157
158fn generate_to_compact(
160 fields: &FieldList,
161 ident: &Ident,
162 zstd: Option<ZstdConfig>,
163 reth_codecs: &syn::Path,
164) -> Vec<TokenStream2> {
165 let mut lines = vec![quote! {
166 let mut buffer = #reth_codecs::__private::bytes::BytesMut::new();
167 }];
168
169 let is_enum = fields.iter().any(|field| matches!(field, FieldTypes::EnumVariant(_)));
170
171 if is_enum {
172 let enum_lines = EnumHandler::new(fields).generate_to(ident);
173
174 lines.push(quote! {
175 flags.set_variant(match self {
176 #(#enum_lines)*
177 });
178 })
179 } else {
180 lines.append(&mut StructHandler::new(fields).generate_to());
181 }
182
183 if zstd.is_some() {
187 lines.push(quote! {
188 let mut zstd = buffer.len() > 7;
189 if zstd {
190 flags.set___zstd(1);
191 }
192 });
193 }
194
195 lines.push(quote! {
197 let flags = flags.into_bytes();
198 total_length += flags.len() + buffer.len();
199 buf.put_slice(&flags);
200 });
201
202 if let Some(zstd) = zstd {
203 let compressor = zstd.compressor;
204 lines.push(quote! {
205 if zstd {
206 #compressor.with(|compressor| {
207 let mut compressor = compressor.borrow_mut();
208
209 let compressed = compressor.compress(&buffer).expect("Failed to compress.");
210 buf.put(compressed.as_slice());
211 });
212 } else {
213 buf.put(buffer);
214 }
215 });
216 } else {
217 lines.push(quote! {
218 buf.put(buffer);
219 })
220 }
221
222 lines
223}
224
225pub(crate) fn parse_reth_codecs_path(attrs: &[Attribute]) -> syn::Result<syn::Path> {
227 let mut reth_codecs_path: syn::Path = syn::parse_quote!(reth_codecs);
229 for attr in attrs {
230 if attr.path().is_ident("reth_codecs") {
231 attr.parse_nested_meta(|meta| {
232 if meta.path.is_ident("crate") {
233 let value = meta.value()?;
234 let lit: LitStr = value.parse()?;
235 reth_codecs_path = syn::parse_str(&lit.value())?;
236 Ok(())
237 } else {
238 Err(meta.error("unsupported attribute"))
239 }
240 })?;
241 }
242 }
243
244 Ok(reth_codecs_path)
245}