parity_scale_codec_derive/
max_encoded_len.rs1#![cfg(feature = "max-encoded-len")]
17
18use crate::{
19 trait_bounds,
20 utils::{codec_crate_path, custom_mel_trait_bound, has_dumb_trait_bound, should_skip},
21};
22use quote::{quote, quote_spanned};
23use syn::{parse_quote, spanned::Spanned, Data, DeriveInput, Fields, Type};
24
25pub fn derive_max_encoded_len(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
27 let mut input: DeriveInput = match syn::parse(input) {
28 Ok(input) => input,
29 Err(e) => return e.to_compile_error().into(),
30 };
31
32 let crate_path = match codec_crate_path(&input.attrs) {
33 Ok(crate_path) => crate_path,
34 Err(error) => return error.into_compile_error().into(),
35 };
36
37 let name = &input.ident;
38 if let Err(e) = trait_bounds::add(
39 &input.ident,
40 &mut input.generics,
41 &input.data,
42 custom_mel_trait_bound(&input.attrs),
43 parse_quote!(#crate_path::MaxEncodedLen),
44 None,
45 has_dumb_trait_bound(&input.attrs),
46 &crate_path
47 ) {
48 return e.to_compile_error().into()
49 }
50 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
51
52 let data_expr = data_length_expr(&input.data);
53
54 quote::quote!(
55 const _: () = {
56 impl #impl_generics #crate_path::MaxEncodedLen for #name #ty_generics #where_clause {
57 fn max_encoded_len() -> ::core::primitive::usize {
58 #data_expr
59 }
60 }
61 };
62 )
63 .into()
64}
65
66fn fields_length_expr(fields: &Fields) -> proc_macro2::TokenStream {
68 let type_iter: Box<dyn Iterator<Item = &Type>> = match fields {
69 Fields::Named(ref fields) => Box::new(
70 fields.named.iter().filter_map(|field| if should_skip(&field.attrs) {
71 None
72 } else {
73 Some(&field.ty)
74 })
75 ),
76 Fields::Unnamed(ref fields) => Box::new(
77 fields.unnamed.iter().filter_map(|field| if should_skip(&field.attrs) {
78 None
79 } else {
80 Some(&field.ty)
81 })
82 ),
83 Fields::Unit => Box::new(std::iter::empty()),
84 };
85 let expansion = type_iter.map(|ty| {
96 quote_spanned! {
97 ty.span() => .saturating_add(<#ty>::max_encoded_len())
98 }
99 });
100 quote! {
101 0_usize #( #expansion )*
102 }
103}
104
105fn data_length_expr(data: &Data) -> proc_macro2::TokenStream {
107 match *data {
108 Data::Struct(ref data) => fields_length_expr(&data.fields),
109 Data::Enum(ref data) => {
110 let expansion = data.variants.iter().map(|variant| {
124 let variant_expression = fields_length_expr(&variant.fields);
125 quote! {
126 .max(#variant_expression)
127 }
128 });
129
130 quote! {
131 0_usize #( #expansion )* .saturating_add(1)
132 }
133 },
134 Data::Union(ref data) => {
135 syn::Error::new(data.union_token.span(), "Union types are not supported.")
138 .to_compile_error()
139 },
140 }
141}