ink_codegen/generator/
storage_item.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::{
18    Ident,
19    TokenStream as TokenStream2,
20    TokenStream,
21};
22use quote::{
23    format_ident,
24    quote,
25    quote_spanned,
26    ToTokens,
27};
28use syn::{
29    spanned::Spanned,
30    Data,
31    DataEnum,
32    DataStruct,
33    DataUnion,
34    Field,
35    Fields,
36    Type,
37};
38
39/// Generates code for the storage item.
40#[derive(From, Copy, Clone)]
41pub struct StorageItem<'a> {
42    /// The storage item to generate code for.
43    item: &'a ir::StorageItem,
44}
45
46impl GenerateCode for StorageItem<'_> {
47    /// Generates ink! storage item code.
48    fn generate_code(&self) -> TokenStream2 {
49        let attrs = self.item.attrs();
50        let generated_struct = match self.item.data().clone() {
51            Data::Struct(struct_item) => self.generate_struct(struct_item),
52            Data::Enum(enum_item) => self.generate_enum(enum_item),
53            Data::Union(union_item) => self.generate_union(union_item),
54        };
55
56        let mut derive = quote! {};
57        if self.item.config().derive() {
58            derive = quote! {
59                #[cfg_attr(feature = "std", derive(
60                    ::ink::storage::traits::StorageLayout,
61                ))]
62                #[::ink::scale_derive(TypeInfo)]
63                #[derive(
64                    ::ink::storage::traits::StorableHint,
65                    ::ink::storage::traits::StorageKey,
66                    ::ink::storage::traits::Storable,
67                )]
68            };
69        }
70
71        let type_check = self.generate_type_check();
72
73        quote! {
74            #type_check
75
76            #(#attrs)*
77            #derive
78            #generated_struct
79        }
80    }
81}
82
83impl StorageItem<'_> {
84    fn generate_struct(&self, struct_item: DataStruct) -> TokenStream2 {
85        let item = self.item;
86        let struct_ident = item.ident();
87        let vis = item.vis();
88        let generics = item.generics();
89        let salt = item.salt();
90
91        let fields = struct_item.fields.iter().enumerate().map(|(i, field)| {
92            convert_into_storage_field(struct_ident, None, &salt, i, field)
93        });
94
95        match struct_item.fields {
96            Fields::Unnamed(_) => {
97                quote! {
98                    #vis struct #struct_ident #generics (
99                        #(#fields),*
100                    );
101                }
102            }
103            _ => {
104                quote! {
105                    #vis struct #struct_ident #generics {
106                        #(#fields),*
107                    }
108                }
109            }
110        }
111    }
112
113    fn generate_enum(&self, enum_item: DataEnum) -> TokenStream2 {
114        let item = self.item;
115        let enum_ident = item.ident();
116        let vis = item.vis();
117        let generics = item.generics();
118        let salt = item.salt();
119
120        let variants = enum_item.variants.into_iter().map(|variant| {
121            let attrs = variant.attrs;
122            let variant_ident = &variant.ident;
123            let discriminant = if let Some((eq, expr)) = variant.discriminant {
124                quote! { #eq #expr}
125            } else {
126                quote! {}
127            };
128
129            let fields: Vec<_> = variant
130                .fields
131                .iter()
132                .enumerate()
133                .map(|(i, field)| {
134                    convert_into_storage_field(
135                        enum_ident,
136                        Some(variant_ident),
137                        &salt,
138                        i,
139                        field,
140                    )
141                })
142                .collect();
143
144            let fields = match variant.fields {
145                Fields::Named(_) => quote! { { #(#fields),* } },
146                Fields::Unnamed(_) => quote! { ( #(#fields),* ) },
147                Fields::Unit => quote! {},
148            };
149
150            quote! {
151                #(#attrs)*
152                #variant_ident #fields #discriminant
153            }
154        });
155
156        quote! {
157            #vis enum #enum_ident #generics {
158                #(#variants),*
159            }
160        }
161    }
162
163    fn generate_union(&self, union_item: DataUnion) -> TokenStream2 {
164        let item = self.item;
165        let union_ident = item.ident();
166        let vis = item.vis();
167        let generics = item.generics();
168        let salt = item.salt();
169
170        let fields = union_item
171            .fields
172            .named
173            .iter()
174            .enumerate()
175            .map(|(i, field)| {
176                convert_into_storage_field(union_ident, None, &salt, i, field)
177            });
178
179        quote! {
180            #vis union #union_ident #generics {
181                #(#fields),*
182            }
183        }
184    }
185
186    fn generate_type_check(&self) -> TokenStream2 {
187        let fields = self
188            .item
189            .all_used_types()
190            .into_iter()
191            .enumerate()
192            .map(|(i, ty)| {
193                let field_name = format_ident!("field_{}", i);
194                let span = ty.span();
195                quote_spanned!(span =>
196                    #field_name: #ty
197                )
198            });
199        let generics = self.item.generics();
200        let salt = self.item.salt();
201
202        quote! {
203            const _: () = {
204                struct Check #generics {
205                    salt: #salt,
206                    #(#fields),*
207                }
208            };
209        }
210    }
211}
212
213fn convert_into_storage_field(
214    struct_ident: &Ident,
215    variant_ident: Option<&syn::Ident>,
216    salt: &TokenStream,
217    index: usize,
218    field: &Field,
219) -> Field {
220    let field_name = if let Some(field_ident) = &field.ident {
221        field_ident.to_string()
222    } else {
223        index.to_string()
224    };
225
226    let variant_name = if let Some(variant_ident) = variant_ident {
227        variant_ident.to_string()
228    } else {
229        "".to_string()
230    };
231
232    let key = ink_primitives::KeyComposer::compute_key(
233        struct_ident.to_string().as_str(),
234        variant_name.as_str(),
235        field_name.as_str(),
236    )
237    .expect("unable to compute the storage key for the field");
238
239    let mut new_field = field.clone();
240    let ty = field.ty.clone().to_token_stream();
241    let span = field.ty.span();
242    let new_ty = Type::Verbatim(quote_spanned!(span =>
243        <#ty as ::ink::storage::traits::AutoStorableHint<
244            ::ink::storage::traits::ManualKey<#key, #salt>,
245        >>::Type
246    ));
247    new_field.ty = new_ty;
248    new_field
249}