ink_macro/storage/
storage_layout.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::quote;
17use syn::spanned::Spanned;
18
19fn field_layout<'a>(
20    variant: &'a synstructure::VariantInfo,
21) -> impl Iterator<Item = TokenStream2> + 'a {
22    variant.ast().fields.iter().enumerate().map(|(i, field)| {
23        let ident = match field.ident.as_ref() {
24            Some(ident) => {
25                let ident_str = ident.to_string();
26                quote! { #ident_str }
27            }
28            None => {
29                let index = i.to_string();
30                quote! { #index }
31            }
32        };
33        let ty = &field.ty;
34        quote! {
35            ::ink::metadata::layout::FieldLayout::new(
36                #ident,
37                <#ty as ::ink::storage::traits::StorageLayout>::layout(__key),
38            )
39        }
40    })
41}
42
43fn storage_layout_struct(s: &synstructure::Structure) -> TokenStream2 {
44    assert!(
45        matches!(s.ast().data, syn::Data::Struct(_)),
46        "s must be a struct item"
47    );
48    assert!(
49        s.variants().len() == 1,
50        "structs must have at most one variant"
51    );
52    let struct_ident = s.ast().ident.clone();
53    let variant: &synstructure::VariantInfo = &s.variants()[0];
54    let field_layouts = field_layout(variant);
55    s.gen_impl(quote! {
56        gen impl ::ink::storage::traits::StorageLayout for @Self {
57            fn layout(__key: &::ink::primitives::Key) -> ::ink::metadata::layout::Layout {
58                ::ink::metadata::layout::Layout::Struct(
59                    ::ink::metadata::layout::StructLayout::new(
60                        ::core::stringify!(#struct_ident),
61                        [
62                            #(#field_layouts ,)*
63                        ]
64                    )
65                )
66            }
67        }
68    })
69}
70
71fn storage_layout_enum(s: &synstructure::Structure) -> TokenStream2 {
72    assert!(
73        matches!(s.ast().data, syn::Data::Enum(_)),
74        "s must be an enum item"
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 variant_layouts = s.variants().iter().enumerate().map(|(n, variant)| {
86        let variant_ident = variant.ast().ident;
87        let discriminant = variant
88            .ast()
89            .discriminant
90            .as_ref()
91            .map(|(_, expr)| quote! { #expr })
92            .unwrap_or_else(|| quote! { #n });
93        let field_layouts = field_layout(variant);
94        quote! {
95            {
96                (
97                    ::ink::metadata::layout::Discriminant::from(#discriminant),
98                    ::ink::metadata::layout::StructLayout::new(
99                        ::core::stringify!(#variant_ident),
100                        [
101                            #(#field_layouts ,)*
102                        ]
103                    ),
104                )
105            }
106        }
107    });
108    let enum_ident = s.ast().ident.clone();
109    s.gen_impl(quote! {
110        gen impl ::ink::storage::traits::StorageLayout for @Self {
111            fn layout(__key: &::ink::primitives::Key) -> ::ink::metadata::layout::Layout {
112                ::ink::metadata::layout::Layout::Enum(
113                    ::ink::metadata::layout::EnumLayout::new(
114                        ::core::stringify!(#enum_ident),
115                        ::ink::metadata::layout::LayoutKey::from(__key),
116                        [
117                            #(#variant_layouts ,)*
118                        ]
119                    )
120                )
121            }
122        }
123    })
124}
125
126pub fn storage_layout_derive(mut s: synstructure::Structure) -> TokenStream2 {
127    s.bind_with(|_| synstructure::BindStyle::Move)
128        .add_bounds(synstructure::AddBounds::Fields)
129        .underscore_const(true);
130    match &s.ast().data {
131        syn::Data::Struct(_) => storage_layout_struct(&s),
132        syn::Data::Enum(_) => storage_layout_enum(&s),
133        _ => panic!("cannot derive `StorageLayout` for Rust `union` items"),
134    }
135}