use proc_macro2::TokenStream as TokenStream2;
use quote::{
quote,
quote_spanned,
};
use syn::spanned::Spanned;
fn storable_struct_derive(s: &synstructure::Structure) -> TokenStream2 {
assert_eq!(s.variants().len(), 1, "can only operate on structs");
let variant: &synstructure::VariantInfo = &s.variants()[0];
let decode_body = variant.construct(|field, _index| {
let ty = &field.ty;
let span = ty.span();
quote_spanned!(span =>
<#ty as ::ink::storage::traits::Storable>::decode(__input)?
)
});
let encode_body = variant.each(|binding| {
let span = binding.ast().ty.span();
quote_spanned!(span =>
::ink::storage::traits::Storable::encode(#binding, __dest);
)
});
let encoded_size_body =
variant.fold(quote!(::core::primitive::usize::MIN), |acc, binding| {
let span = binding.ast().ty.span();
quote_spanned!(span =>
#acc.saturating_add(::ink::storage::traits::Storable::encoded_size(#binding))
)
});
s.gen_impl(quote! {
gen impl ::ink::storage::traits::Storable for @Self {
#[inline(always)]
#[allow(non_camel_case_types)]
fn decode<__ink_I: ::ink::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result<Self, ::ink::scale::Error> {
::core::result::Result::Ok(#decode_body)
}
#[inline(always)]
#[allow(non_camel_case_types)]
fn encode<__ink_O: ::ink::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) {
match self { #encode_body }
}
#[inline(always)]
#[allow(non_camel_case_types)]
fn encoded_size(&self) -> ::core::primitive::usize {
match self { #encoded_size_body }
}
}
})
}
fn storable_enum_derive(s: &synstructure::Structure) -> TokenStream2 {
assert!(
!s.variants().is_empty(),
"encountered invalid empty enum type deriving Storable trait"
);
if s.variants().len() > 256 {
return syn::Error::new(
s.ast().span(),
"Currently only enums with at most 256 variants are supported.",
)
.to_compile_error()
}
let decode_body = s
.variants()
.iter()
.map(|variant| {
variant.construct(|field, _index| {
let ty = &field.ty;
let span = ty.span();
quote_spanned!(span =>
<#ty as ::ink::storage::traits::Storable>::decode(__input)?
)
})
})
.enumerate()
.fold(quote! {}, |acc, (index, variant)| {
let index = index as u8;
quote! {
#acc
#index => #variant,
}
});
let encode_body = s.variants().iter().enumerate().map(|(index, variant)| {
let pat = variant.pat();
let index = index as u8;
let fields = variant.bindings().iter().map(|field| {
let span = field.ast().ty.span();
quote_spanned!(span =>
::ink::storage::traits::Storable::encode(#field, __dest);
)
});
quote! {
#pat => {
{ <::core::primitive::u8 as ::ink::storage::traits::Storable>::encode(&#index, __dest); }
#(
{ #fields }
)*
}
}
});
let encoded_size_body = s.variants().iter().map(|variant| {
let pat = variant.pat();
let field = variant.bindings().iter().fold(quote!(1usize), |acc, field| {
let span = field.ast().ty.span();
quote_spanned!(span =>
#acc.saturating_add(::ink::storage::traits::Storable::encoded_size(#field))
)
});
quote! {
#pat => { #field }
}
});
s.gen_impl(quote! {
gen impl ::ink::storage::traits::Storable for @Self {
#[inline(always)]
#[allow(non_camel_case_types)]
fn decode<__ink_I: ::ink::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result<Self, ::ink::scale::Error> {
::core::result::Result::Ok(
match <::core::primitive::u8 as ::ink::storage::traits::Storable>::decode(__input)? {
#decode_body
_ => unreachable!("encountered invalid enum discriminant"),
}
)
}
#[inline(always)]
#[allow(non_camel_case_types)]
fn encode<__ink_O: ::ink::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) {
match self {
#(
#encode_body
)*
}
}
#[inline(always)]
#[allow(non_camel_case_types)]
fn encoded_size(&self) -> ::core::primitive::usize {
match self {
#(
#encoded_size_body
)*
}
}
}
})
}
pub fn storable_derive(mut s: synstructure::Structure) -> TokenStream2 {
s.bind_with(|_| synstructure::BindStyle::Move)
.add_bounds(synstructure::AddBounds::Fields)
.underscore_const(true);
match &s.ast().data {
syn::Data::Struct(_) => storable_struct_derive(&s),
syn::Data::Enum(_) => storable_enum_derive(&s),
_ => {
panic!("cannot derive `Storable` for Rust `union` items")
}
}
}