ink_codegen/generator/
storage.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 crate::GenerateCode;
16use derive_more::From;
17use proc_macro2::TokenStream as TokenStream2;
18use quote::{
19    quote,
20    quote_spanned,
21};
22use syn::spanned::Spanned as _;
23
24/// Generator to create the ink! storage struct and important trait implementations.
25#[derive(From)]
26pub struct Storage<'a> {
27    contract: &'a ir::Contract,
28}
29impl_as_ref_for_generator!(Storage);
30
31impl GenerateCode for Storage<'_> {
32    fn generate_code(&self) -> TokenStream2 {
33        let storage_span = self.contract.module().storage().span();
34        let access_env_impls = self.generate_access_env_trait_impls();
35        let storage_struct = self.generate_storage_struct();
36        quote_spanned!(storage_span =>
37            #storage_struct
38            #access_env_impls
39
40            const _: () = {
41                // Used to make `self.env()` and `Self::env()` available in message code.
42                #[allow(unused_imports)]
43                use ::ink::codegen::{
44                    Env as _,
45                    StaticEnv as _,
46                };
47            };
48        )
49    }
50}
51
52impl Storage<'_> {
53    fn generate_access_env_trait_impls(&self) -> TokenStream2 {
54        let storage_ident = &self.contract.module().storage().ident();
55        quote! {
56            const _: () = {
57                impl<'a> ::ink::codegen::Env for &'a #storage_ident {
58                    type EnvAccess = ::ink::EnvAccess<
59                        'a, <#storage_ident as ::ink::env::ContractEnv>::Env>;
60
61                    fn env(self) -> Self::EnvAccess {
62                        <<Self as ::ink::codegen::Env>::EnvAccess
63                            as ::core::default::Default>::default()
64                    }
65                }
66
67                impl<'a> ::ink::codegen::StaticEnv for #storage_ident {
68                    type EnvAccess = ::ink::EnvAccess<
69                        'static, <#storage_ident as ::ink::env::ContractEnv>::Env>;
70
71                    fn env() -> Self::EnvAccess {
72                        <<Self as ::ink::codegen::StaticEnv>::EnvAccess
73                            as ::core::default::Default>::default()
74                    }
75                }
76            };
77        }
78    }
79
80    /// Generates the storage struct definition.
81    ///
82    /// # Developer Note
83    ///
84    /// The `fortanix` config attribute is used here to convey the
85    /// information that the generated struct is an ink! storage struct to `dylint`.
86    ///
87    /// We decided on this attribute to mark the function, as it has to be a
88    /// key-value pair that is well known to `cargo`. `fortanix` seems like an obscure
89    /// vendor, for  which it is highly unlikely that someone will ever compile
90    /// a contract for.
91    fn generate_storage_struct(&self) -> TokenStream2 {
92        let storage = self.contract.module().storage();
93        let span = storage.span();
94        let ident = storage.ident();
95        let generics = storage.generics();
96        let attrs = storage.attrs();
97        let fields = storage.fields();
98        quote_spanned!( span =>
99            #(#attrs)*
100            #[::ink::storage_item]
101            #[cfg_attr(test, derive(::core::fmt::Debug))]
102            #[cfg(not(target_vendor = "fortanix"))]
103            pub struct #ident #generics {
104                #( #fields ),*
105            }
106
107            const _: () = {
108                impl ::ink::reflect::ContractName for #ident {
109                    const NAME: &'static str = ::core::stringify!(#ident);
110                }
111            };
112        )
113    }
114}