ink_macro/storage/
storable.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use proc_macro2::TokenStream as TokenStream2;
16use quote::{
17    quote,
18    quote_spanned,
19};
20use syn::spanned::Spanned;
21
22/// `Storable` derive implementation for `struct` types.
23fn 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
70/// `Storable` derive implementation for `enum` types.
71fn 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
174/// Derives `ink_storage`'s `Storable` trait for the given `struct` or `enum`.
175pub 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}