ink_codegen/generator/
storage_item.rs1use 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#[derive(From, Copy, Clone)]
41pub struct StorageItem<'a> {
42 item: &'a ir::StorageItem,
44}
45
46impl GenerateCode for StorageItem<'_> {
47 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}