ink_macro/storage/
storable.rs1use proc_macro2::TokenStream as TokenStream2;
16use quote::{
17 quote,
18 quote_spanned,
19};
20use syn::spanned::Spanned;
21
22fn storable_struct_derive(s: &synstructure::Structure) -> TokenStream2 {
24 assert_eq!(s.variants().len(), 1, "can only operate on structs");
25 let variant: &synstructure::VariantInfo = &s.variants()[0];
26 let decode_body = variant.construct(|field, _index| {
27 let ty = &field.ty;
28 let span = ty.span();
29 quote_spanned!(span =>
30 <#ty as ::ink::storage::traits::Storable>::decode(__input)?
31 )
32 });
33 let encode_body = variant.each(|binding| {
34 let span = binding.ast().ty.span();
35 quote_spanned!(span =>
36 ::ink::storage::traits::Storable::encode(#binding, __dest);
37 )
38 });
39 let encoded_size_body =
40 variant.fold(quote!(::core::primitive::usize::MIN), |acc, binding| {
41 let span = binding.ast().ty.span();
42 quote_spanned!(span =>
43 #acc.saturating_add(::ink::storage::traits::Storable::encoded_size(#binding))
44 )
45 });
46
47 s.gen_impl(quote! {
48 gen impl ::ink::storage::traits::Storable for @Self {
49 #[inline(always)]
50 #[allow(non_camel_case_types)]
51 fn decode<__ink_I: ::ink::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result<Self, ::ink::scale::Error> {
52 ::core::result::Result::Ok(#decode_body)
53 }
54
55 #[inline(always)]
56 #[allow(non_camel_case_types)]
57 fn encode<__ink_O: ::ink::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) {
58 match self { #encode_body }
59 }
60
61 #[inline(always)]
62 #[allow(non_camel_case_types)]
63 fn encoded_size(&self) -> ::core::primitive::usize {
64 match self { #encoded_size_body }
65 }
66 }
67 })
68}
69
70fn storable_enum_derive(s: &synstructure::Structure) -> TokenStream2 {
72 assert!(
73 !s.variants().is_empty(),
74 "encountered invalid empty enum type deriving Storable trait"
75 );
76
77 if s.variants().len() > 256 {
78 return syn::Error::new(
79 s.ast().span(),
80 "Currently only enums with at most 256 variants are supported.",
81 )
82 .to_compile_error()
83 }
84
85 let decode_body = s
86 .variants()
87 .iter()
88 .map(|variant| {
89 variant.construct(|field, _index| {
90 let ty = &field.ty;
91 let span = ty.span();
92 quote_spanned!(span =>
93 <#ty as ::ink::storage::traits::Storable>::decode(__input)?
94 )
95 })
96 })
97 .enumerate()
98 .fold(quote! {}, |acc, (index, variant)| {
99 let index = index as u8;
100 quote! {
101 #acc
102 #index => #variant,
103 }
104 });
105
106 let encode_body = s.variants().iter().enumerate().map(|(index, variant)| {
107 let pat = variant.pat();
108 let index = index as u8;
109 let fields = variant.bindings().iter().map(|field| {
110 let span = field.ast().ty.span();
111 quote_spanned!(span =>
112 ::ink::storage::traits::Storable::encode(#field, __dest);
113 )
114 });
115 quote! {
116 #pat => {
117 { <::core::primitive::u8 as ::ink::storage::traits::Storable>::encode(&#index, __dest); }
118 #(
119 { #fields }
120 )*
121 }
122 }
123 });
124
125 let encoded_size_body = s.variants().iter().map(|variant| {
126 let pat = variant.pat();
127 let field = variant.bindings().iter().fold(quote!(1usize), |acc, field| {
128 let span = field.ast().ty.span();
129 quote_spanned!(span =>
130 #acc.saturating_add(::ink::storage::traits::Storable::encoded_size(#field))
131 )
132 });
133 quote! {
134 #pat => { #field }
135 }
136 });
137
138 s.gen_impl(quote! {
139 gen impl ::ink::storage::traits::Storable for @Self {
140 #[inline(always)]
141 #[allow(non_camel_case_types)]
142 fn decode<__ink_I: ::ink::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result<Self, ::ink::scale::Error> {
143 ::core::result::Result::Ok(
144 match <::core::primitive::u8 as ::ink::storage::traits::Storable>::decode(__input)? {
145 #decode_body
146 _ => unreachable!("encountered invalid enum discriminant"),
147 }
148 )
149 }
150
151 #[inline(always)]
152 #[allow(non_camel_case_types)]
153 fn encode<__ink_O: ::ink::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) {
154 match self {
155 #(
156 #encode_body
157 )*
158 }
159 }
160
161 #[inline(always)]
162 #[allow(non_camel_case_types)]
163 fn encoded_size(&self) -> ::core::primitive::usize {
164 match self {
165 #(
166 #encoded_size_body
167 )*
168 }
169 }
170 }
171 })
172}
173
174pub fn storable_derive(mut s: synstructure::Structure) -> TokenStream2 {
176 s.bind_with(|_| synstructure::BindStyle::Move)
177 .add_bounds(synstructure::AddBounds::Fields)
178 .underscore_const(true);
179 match &s.ast().data {
180 syn::Data::Struct(_) => storable_struct_derive(&s),
181 syn::Data::Enum(_) => storable_enum_derive(&s),
182 _ => {
183 panic!("cannot derive `Storable` for Rust `union` items")
184 }
185 }
186}