alloy_rlp_derive/
de.rs
1use crate::utils::{
2 attributes_include, field_ident, is_optional, make_generics, parse_struct, EMPTY_STRING_CODE,
3};
4use proc_macro2::TokenStream;
5use quote::quote;
6use syn::{Error, Result};
7
8pub(crate) fn impl_decodable(ast: &syn::DeriveInput) -> Result<TokenStream> {
9 let body = parse_struct(ast, "RlpDecodable")?;
10
11 let fields = body.fields.iter().enumerate();
12
13 let supports_trailing_opt = attributes_include(&ast.attrs, "trailing");
14
15 let mut encountered_opt_item = false;
16 let mut decode_stmts = Vec::with_capacity(body.fields.len());
17 for (i, field) in fields {
18 let is_opt = is_optional(field);
19 if is_opt {
20 if !supports_trailing_opt {
21 let msg = "optional fields are disabled.\nAdd the `#[rlp(trailing)]` attribute to the struct in order to enable optional fields";
22 return Err(Error::new_spanned(field, msg));
23 }
24 encountered_opt_item = true;
25 } else if encountered_opt_item && !attributes_include(&field.attrs, "default") {
26 let msg =
27 "all the fields after the first optional field must be either optional or default";
28 return Err(Error::new_spanned(field, msg));
29 }
30
31 decode_stmts.push(decodable_field(i, field, is_opt));
32 }
33
34 let name = &ast.ident;
35 let generics = make_generics(&ast.generics, quote!(alloy_rlp::Decodable));
36 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
37
38 Ok(quote! {
39 const _: () = {
40 extern crate alloy_rlp;
41
42 impl #impl_generics alloy_rlp::Decodable for #name #ty_generics #where_clause {
43 #[inline]
44 fn decode(b: &mut &[u8]) -> alloy_rlp::Result<Self> {
45 let alloy_rlp::Header { list, payload_length } = alloy_rlp::Header::decode(b)?;
46 if !list {
47 return Err(alloy_rlp::Error::UnexpectedString);
48 }
49
50 let started_len = b.len();
51 if started_len < payload_length {
52 return Err(alloy_rlp::DecodeError::InputTooShort);
53 }
54
55 let this = Self {
56 #(#decode_stmts)*
57 };
58
59 let consumed = started_len - b.len();
60 if consumed != payload_length {
61 return Err(alloy_rlp::Error::ListLengthMismatch {
62 expected: payload_length,
63 got: consumed,
64 });
65 }
66
67 Ok(this)
68 }
69 }
70 };
71 })
72}
73
74pub(crate) fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> Result<TokenStream> {
75 let body = parse_struct(ast, "RlpEncodableWrapper")?;
76
77 if body.fields.iter().count() != 1 {
78 let msg = "`RlpEncodableWrapper` is only defined for structs with one field.";
79 return Err(Error::new(ast.ident.span(), msg));
80 }
81
82 let name = &ast.ident;
83 let generics = make_generics(&ast.generics, quote!(alloy_rlp::Decodable));
84 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
85
86 Ok(quote! {
87 const _: () = {
88 extern crate alloy_rlp;
89
90 impl #impl_generics alloy_rlp::Decodable for #name #ty_generics #where_clause {
91 #[inline]
92 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
93 alloy_rlp::private::Result::map(alloy_rlp::Decodable::decode(buf), Self)
94 }
95 }
96 };
97 })
98}
99
100fn decodable_field(index: usize, field: &syn::Field, is_opt: bool) -> TokenStream {
101 let ident = field_ident(index, field);
102
103 if attributes_include(&field.attrs, "default") {
104 quote! { #ident: alloy_rlp::private::Default::default(), }
105 } else if is_opt {
106 quote! {
107 #ident: if started_len - b.len() < payload_length {
108 if alloy_rlp::private::Option::map_or(b.first(), false, |b| *b == #EMPTY_STRING_CODE) {
109 alloy_rlp::Buf::advance(b, 1);
110 None
111 } else {
112 Some(alloy_rlp::Decodable::decode(b)?)
113 }
114 } else {
115 None
116 },
117 }
118 } else {
119 quote! { #ident: alloy_rlp::Decodable::decode(b)?, }
120 }
121}