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 `__ink_StorageMarker` trait marks the generated `struct` as an ink! storage
85    /// `struct`.
86    ///
87    /// This marker trait can be used to find the ink! storage struct during code analysis
88    /// stages of the compilation pipeline (e.g. in the `ink_linting` infrastructure).
89    ///
90    /// This approach is similar to Rust's use of [marker][rust-markers] traits
91    /// to express that a type satisfies some property.
92    ///
93    /// [rust-markers]: https://doc.rust-lang.org/std/marker/index.html
94    fn generate_storage_struct(&self) -> TokenStream2 {
95        let storage = self.contract.module().storage();
96        let span = storage.span();
97        let ident = storage.ident();
98        let generics = storage.generics();
99        let attrs = storage.attrs();
100        let fields = storage.fields();
101        quote_spanned!( span =>
102            #(#attrs)*
103            #[::ink::storage_item]
104            #[cfg_attr(test, derive(::core::fmt::Debug))]
105            pub struct #ident #generics {
106                #( #fields ),*
107            }
108
109            const _: () = {
110                impl ::ink::reflect::ContractName for #ident {
111                    const NAME: &'static str = ::core::stringify!(#ident);
112                }
113
114                #[allow(non_camel_case_types)]
115                trait __ink_StorageMarker {}
116                impl __ink_StorageMarker for #ident {}
117            };
118        )
119    }
120}