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}