1use crate::{counter_prefix, pallet::parse::helper};
21use frame_support_procedural_tools::generate_access_from_frame_or_crate;
22use proc_macro2::{Span, TokenStream};
23use quote::{quote, ToTokens};
24use syn::{
25 parenthesized,
26 parse::{Parse, ParseStream},
27 punctuated::Punctuated,
28 spanned::Spanned,
29 token,
30 visit::Visit,
31 Attribute, Error, Ident, Result, Token, Type, TypeParam, Visibility, WhereClause,
32};
33
34trait TypeExt {
36 fn get_ident(&self) -> Option<&Ident>;
37 fn contains_ident(&self, ident: &Ident) -> bool;
38}
39
40impl TypeExt for Type {
41 fn get_ident(&self) -> Option<&Ident> {
42 match self {
43 Type::Path(p) => match &p.qself {
44 Some(qself) => qself.ty.get_ident(),
45 None => p.path.get_ident(),
46 },
47 _ => None,
48 }
49 }
50
51 fn contains_ident(&self, ident: &Ident) -> bool {
52 struct ContainsIdent<'a> {
53 ident: &'a Ident,
54 found: bool,
55 }
56 impl<'a, 'ast> Visit<'ast> for ContainsIdent<'a> {
57 fn visit_ident(&mut self, i: &'ast Ident) {
58 if i == self.ident {
59 self.found = true;
60 }
61 }
62 }
63
64 let mut visitor = ContainsIdent { ident, found: false };
65 syn::visit::visit_type(&mut visitor, self);
66 visitor.found
67 }
68}
69
70struct SimpleGenerics {
72 lt_token: Token![<],
73 params: Punctuated<TypeParam, token::Comma>,
74 gt_token: Token![>],
75}
76
77impl SimpleGenerics {
78 fn type_generics(&self) -> impl Iterator<Item = &Ident> {
80 self.params.iter().map(|p| &p.ident)
81 }
82
83 fn impl_generics(&self) -> impl Iterator<Item = &TypeParam> {
85 self.params.iter()
86 }
87}
88
89impl Parse for SimpleGenerics {
90 fn parse(input: ParseStream<'_>) -> Result<Self> {
91 Ok(Self {
92 lt_token: input.parse()?,
93 params: Punctuated::parse_separated_nonempty(input)?,
94 gt_token: input.parse()?,
95 })
96 }
97}
98
99impl ToTokens for SimpleGenerics {
100 fn to_tokens(&self, tokens: &mut TokenStream) {
101 self.lt_token.to_tokens(tokens);
102 self.params.to_tokens(tokens);
103 self.gt_token.to_tokens(tokens);
104 }
105}
106
107mod storage_types {
108 syn::custom_keyword!(StorageValue);
109 syn::custom_keyword!(StorageMap);
110 syn::custom_keyword!(CountedStorageMap);
111 syn::custom_keyword!(StorageDoubleMap);
112 syn::custom_keyword!(StorageNMap);
113}
114
115mod prefix_types {
117 syn::custom_keyword!(verbatim);
119 syn::custom_keyword!(pallet_name);
121 syn::custom_keyword!(dynamic);
123}
124
125enum StorageType {
127 Value {
128 _kw: storage_types::StorageValue,
129 _lt_token: Token![<],
130 prefix: Type,
131 _value_comma: Token![,],
132 value_ty: Type,
133 query_type: Option<(Token![,], Type)>,
134 _trailing_comma: Option<Token![,]>,
135 _gt_token: Token![>],
136 },
137 Map {
138 _kw: storage_types::StorageMap,
139 _lt_token: Token![<],
140 prefix: Type,
141 _hasher_comma: Token![,],
142 hasher_ty: Type,
143 _key_comma: Token![,],
144 key_ty: Type,
145 _value_comma: Token![,],
146 value_ty: Type,
147 query_type: Option<(Token![,], Type)>,
148 _trailing_comma: Option<Token![,]>,
149 _gt_token: Token![>],
150 },
151 CountedMap {
152 _kw: storage_types::CountedStorageMap,
153 _lt_token: Token![<],
154 prefix: Type,
155 _hasher_comma: Token![,],
156 hasher_ty: Type,
157 _key_comma: Token![,],
158 key_ty: Type,
159 _value_comma: Token![,],
160 value_ty: Type,
161 query_type: Option<(Token![,], Type)>,
162 _trailing_comma: Option<Token![,]>,
163 _gt_token: Token![>],
164 },
165 DoubleMap {
166 _kw: storage_types::StorageDoubleMap,
167 _lt_token: Token![<],
168 prefix: Type,
169 _hasher1_comma: Token![,],
170 hasher1_ty: Type,
171 _key1_comma: Token![,],
172 key1_ty: Type,
173 _hasher2_comma: Token![,],
174 hasher2_ty: Type,
175 _key2_comma: Token![,],
176 key2_ty: Type,
177 _value_comma: Token![,],
178 value_ty: Type,
179 query_type: Option<(Token![,], Type)>,
180 _trailing_comma: Option<Token![,]>,
181 _gt_token: Token![>],
182 },
183 NMap {
184 _kw: storage_types::StorageNMap,
185 _lt_token: Token![<],
186 prefix: Type,
187 _paren_comma: Token![,],
188 _paren_token: token::Paren,
189 key_types: Punctuated<Type, Token![,]>,
190 _value_comma: Token![,],
191 value_ty: Type,
192 query_type: Option<(Token![,], Type)>,
193 _trailing_comma: Option<Token![,]>,
194 _gt_token: Token![>],
195 },
196}
197
198impl StorageType {
199 fn generate_type_declaration(
201 &self,
202 crate_: &syn::Path,
203 storage_instance: &StorageInstance,
204 storage_name: &Ident,
205 storage_generics: Option<&SimpleGenerics>,
206 visibility: &Visibility,
207 attributes: &[Attribute],
208 ) -> TokenStream {
209 let storage_instance_generics = &storage_instance.generics;
210 let storage_instance = &storage_instance.name;
211 let attributes = attributes.iter();
212 let storage_generics = storage_generics.map(|g| {
213 let generics = g.type_generics();
214
215 quote!( < #( #generics ),* > )
216 });
217
218 match self {
219 Self::Value { value_ty, query_type, .. } => {
220 let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
221
222 quote! {
223 #( #attributes )*
224 #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageValue<
225 #storage_instance #storage_instance_generics,
226 #value_ty
227 #query_type
228 >;
229 }
230 },
231 Self::CountedMap { value_ty, query_type, hasher_ty, key_ty, .. } |
232 Self::Map { value_ty, query_type, hasher_ty, key_ty, .. } => {
233 let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
234 let map_type = Ident::new(
235 match self {
236 Self::Map { .. } => "StorageMap",
237 _ => "CountedStorageMap",
238 },
239 Span::call_site(),
240 );
241
242 quote! {
243 #( #attributes )*
244 #visibility type #storage_name #storage_generics = #crate_::storage::types::#map_type<
245 #storage_instance #storage_instance_generics,
246 #hasher_ty,
247 #key_ty,
248 #value_ty
249 #query_type
250 >;
251 }
252 },
253 Self::DoubleMap {
254 value_ty,
255 query_type,
256 hasher1_ty,
257 key1_ty,
258 hasher2_ty,
259 key2_ty,
260 ..
261 } => {
262 let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
263
264 quote! {
265 #( #attributes )*
266 #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageDoubleMap<
267 #storage_instance #storage_instance_generics,
268 #hasher1_ty,
269 #key1_ty,
270 #hasher2_ty,
271 #key2_ty,
272 #value_ty
273 #query_type
274 >;
275 }
276 },
277 Self::NMap { value_ty, query_type, key_types, .. } => {
278 let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
279 let key_types = key_types.iter();
280
281 quote! {
282 #( #attributes )*
283 #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageNMap<
284 #storage_instance #storage_instance_generics,
285 ( #( #key_types ),* ),
286 #value_ty
287 #query_type
288 >;
289 }
290 },
291 }
292 }
293
294 fn prefix(&self) -> &Type {
296 match self {
297 Self::Value { prefix, .. } |
298 Self::Map { prefix, .. } |
299 Self::CountedMap { prefix, .. } |
300 Self::NMap { prefix, .. } |
301 Self::DoubleMap { prefix, .. } => prefix,
302 }
303 }
304}
305
306impl Parse for StorageType {
307 fn parse(input: ParseStream<'_>) -> Result<Self> {
308 let lookahead = input.lookahead1();
309
310 let parse_query_type = |input: ParseStream<'_>| -> Result<Option<(Token![,], Type)>> {
311 if input.peek(Token![,]) && !input.peek2(Token![>]) {
312 Ok(Some((input.parse()?, input.parse()?)))
313 } else {
314 Ok(None)
315 }
316 };
317
318 if lookahead.peek(storage_types::StorageValue) {
319 Ok(Self::Value {
320 _kw: input.parse()?,
321 _lt_token: input.parse()?,
322 prefix: input.parse()?,
323 _value_comma: input.parse()?,
324 value_ty: input.parse()?,
325 query_type: parse_query_type(input)?,
326 _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
327 _gt_token: input.parse()?,
328 })
329 } else if lookahead.peek(storage_types::StorageMap) {
330 Ok(Self::Map {
331 _kw: input.parse()?,
332 _lt_token: input.parse()?,
333 prefix: input.parse()?,
334 _hasher_comma: input.parse()?,
335 hasher_ty: input.parse()?,
336 _key_comma: input.parse()?,
337 key_ty: input.parse()?,
338 _value_comma: input.parse()?,
339 value_ty: input.parse()?,
340 query_type: parse_query_type(input)?,
341 _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
342 _gt_token: input.parse()?,
343 })
344 } else if lookahead.peek(storage_types::CountedStorageMap) {
345 Ok(Self::CountedMap {
346 _kw: input.parse()?,
347 _lt_token: input.parse()?,
348 prefix: input.parse()?,
349 _hasher_comma: input.parse()?,
350 hasher_ty: input.parse()?,
351 _key_comma: input.parse()?,
352 key_ty: input.parse()?,
353 _value_comma: input.parse()?,
354 value_ty: input.parse()?,
355 query_type: parse_query_type(input)?,
356 _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
357 _gt_token: input.parse()?,
358 })
359 } else if lookahead.peek(storage_types::StorageDoubleMap) {
360 Ok(Self::DoubleMap {
361 _kw: input.parse()?,
362 _lt_token: input.parse()?,
363 prefix: input.parse()?,
364 _hasher1_comma: input.parse()?,
365 hasher1_ty: input.parse()?,
366 _key1_comma: input.parse()?,
367 key1_ty: input.parse()?,
368 _hasher2_comma: input.parse()?,
369 hasher2_ty: input.parse()?,
370 _key2_comma: input.parse()?,
371 key2_ty: input.parse()?,
372 _value_comma: input.parse()?,
373 value_ty: input.parse()?,
374 query_type: parse_query_type(input)?,
375 _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
376 _gt_token: input.parse()?,
377 })
378 } else if lookahead.peek(storage_types::StorageNMap) {
379 let content;
380 Ok(Self::NMap {
381 _kw: input.parse()?,
382 _lt_token: input.parse()?,
383 prefix: input.parse()?,
384 _paren_comma: input.parse()?,
385 _paren_token: parenthesized!(content in input),
386 key_types: Punctuated::parse_terminated(&content)?,
387 _value_comma: input.parse()?,
388 value_ty: input.parse()?,
389 query_type: parse_query_type(input)?,
390 _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
391 _gt_token: input.parse()?,
392 })
393 } else {
394 Err(lookahead.error())
395 }
396 }
397}
398
399struct Input {
401 attributes: Vec<Attribute>,
402 visibility: Visibility,
403 _type: Token![type],
404 storage_name: Ident,
405 storage_generics: Option<SimpleGenerics>,
406 where_clause: Option<WhereClause>,
407 _equal: Token![=],
408 storage_type: StorageType,
409 _semicolon: Token![;],
410}
411
412impl Parse for Input {
413 fn parse(input: ParseStream<'_>) -> Result<Self> {
414 let attributes = input.call(Attribute::parse_outer)?;
415 let visibility = input.parse()?;
416 let _type = input.parse()?;
417 let storage_name = input.parse()?;
418
419 let lookahead = input.lookahead1();
420 let storage_generics = if lookahead.peek(Token![<]) {
421 Some(input.parse()?)
422 } else if lookahead.peek(Token![=]) {
423 None
424 } else {
425 return Err(lookahead.error())
426 };
427
428 let lookahead = input.lookahead1();
429 let where_clause = if lookahead.peek(Token![where]) {
430 Some(input.parse()?)
431 } else if lookahead.peek(Token![=]) {
432 None
433 } else {
434 return Err(lookahead.error())
435 };
436
437 let _equal = input.parse()?;
438
439 let storage_type = input.parse()?;
440
441 let _semicolon = input.parse()?;
442
443 Ok(Self {
444 attributes,
445 visibility,
446 _type,
447 storage_name,
448 storage_generics,
449 _equal,
450 storage_type,
451 where_clause,
452 _semicolon,
453 })
454 }
455}
456
457#[derive(Clone, Copy)]
459enum PrefixType {
460 Compatibility,
465 Verbatim,
467 PalletName,
471 Dynamic,
473}
474
475pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> Result<TokenStream> {
477 let input = syn::parse2::<Input>(input)?;
478 let crate_ = generate_access_from_frame_or_crate("frame-support")?;
479
480 let prefix_type = if attributes.is_empty() {
481 PrefixType::Compatibility
482 } else if syn::parse2::<prefix_types::verbatim>(attributes.clone()).is_ok() {
483 PrefixType::Verbatim
484 } else if syn::parse2::<prefix_types::pallet_name>(attributes.clone()).is_ok() {
485 PrefixType::PalletName
486 } else if syn::parse2::<prefix_types::dynamic>(attributes.clone()).is_ok() {
487 PrefixType::Dynamic
488 } else {
489 return Err(Error::new(attributes.span(), "Unknown attributes"))
490 };
491
492 let storage_instance = generate_storage_instance(
493 &crate_,
494 &input.storage_name,
495 input.storage_generics.as_ref(),
496 input.where_clause.as_ref(),
497 input.storage_type.prefix(),
498 &input.visibility,
499 matches!(input.storage_type, StorageType::CountedMap { .. }),
500 prefix_type,
501 )?;
502
503 let definition = input.storage_type.generate_type_declaration(
504 &crate_,
505 &storage_instance,
506 &input.storage_name,
507 input.storage_generics.as_ref(),
508 &input.visibility,
509 &input.attributes,
510 );
511
512 let storage_instance_code = storage_instance.code;
513
514 Ok(quote! {
515 #storage_instance_code
516
517 #definition
518 })
519}
520
521struct StorageInstance {
523 name: Ident,
524 generics: TokenStream,
525 code: TokenStream,
526}
527
528fn generate_storage_instance(
530 crate_: &syn::Path,
531 storage_name: &Ident,
532 storage_generics: Option<&SimpleGenerics>,
533 storage_where_clause: Option<&WhereClause>,
534 prefix: &Type,
535 visibility: &Visibility,
536 is_counted_map: bool,
537 prefix_type: PrefixType,
538) -> Result<StorageInstance> {
539 if let Type::Infer(_) = prefix {
540 return Err(Error::new(prefix.span(), "`_` is not allowed as prefix by `storage_alias`."))
541 }
542
543 let impl_generics_used_by_prefix = storage_generics
544 .as_ref()
545 .map(|g| {
546 g.impl_generics()
547 .filter(|g| prefix.contains_ident(&g.ident))
548 .collect::<Vec<_>>()
549 })
550 .unwrap_or_default();
551
552 let (pallet_prefix, impl_generics, type_generics) = match prefix_type {
553 PrefixType::Compatibility =>
554 if !impl_generics_used_by_prefix.is_empty() {
555 let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident);
556 let impl_generics = impl_generics_used_by_prefix.iter();
557
558 (
559 quote! {
560 < #prefix as #crate_::traits::PalletInfoAccess>::name()
561 },
562 quote!( #( #impl_generics ),* ),
563 quote!( #( #type_generics ),* ),
564 )
565 } else if let Some(prefix) = prefix.get_ident() {
566 let prefix_str = prefix.to_string();
567
568 (quote!(#prefix_str), quote!(), quote!())
569 } else {
570 return Err(Error::new_spanned(
571 prefix,
572 "If there are no generics, the prefix is only allowed to be an identifier.",
573 ))
574 },
575 PrefixType::Verbatim => {
576 let prefix_str = match prefix.get_ident() {
577 Some(p) => p.to_string(),
578 None =>
579 return Err(Error::new_spanned(
580 prefix,
581 "Prefix type `verbatim` requires that the prefix is an ident.",
582 )),
583 };
584
585 (quote!(#prefix_str), quote!(), quote!())
586 },
587 PrefixType::PalletName => {
588 let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident);
589 let impl_generics = impl_generics_used_by_prefix.iter();
590
591 (
592 quote! {
593 <#prefix as #crate_::traits::PalletInfoAccess>::name()
594 },
595 quote!( #( #impl_generics ),* ),
596 quote!( #( #type_generics ),* ),
597 )
598 },
599 PrefixType::Dynamic => {
600 let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident);
601 let impl_generics = impl_generics_used_by_prefix.iter();
602
603 (
604 quote! {
605 <#prefix as #crate_::traits::Get<_>>::get()
606 },
607 quote!( #( #impl_generics ),* ),
608 quote!( #( #type_generics ),* ),
609 )
610 },
611 };
612
613 let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default();
614
615 let name_str = format!("{}_Storage_Instance", storage_name);
616 let name = Ident::new(&name_str, Span::call_site());
617 let storage_name_str = storage_name.to_string();
618
619 let counter_code = is_counted_map.then(|| {
620 let counter_name = Ident::new(&counter_prefix(&name_str), Span::call_site());
621 let counter_storage_name_str = counter_prefix(&storage_name_str);
622 let storage_prefix_hash = helper::two128_str(&counter_storage_name_str);
623
624 quote! {
625 #visibility struct #counter_name< #impl_generics >(
626 ::core::marker::PhantomData<(#type_generics)>
627 ) #where_clause;
628
629 impl<#impl_generics> #crate_::traits::StorageInstance
630 for #counter_name< #type_generics > #where_clause
631 {
632 fn pallet_prefix() -> &'static str {
633 #pallet_prefix
634 }
635
636 const STORAGE_PREFIX: &'static str = #counter_storage_name_str;
637 fn storage_prefix_hash() -> [u8; 16] {
638 #storage_prefix_hash
639 }
640 }
641
642 impl<#impl_generics> #crate_::storage::types::CountedStorageMapInstance
643 for #name< #type_generics > #where_clause
644 {
645 type CounterPrefix = #counter_name < #type_generics >;
646 }
647 }
648 });
649
650 let storage_prefix_hash = helper::two128_str(&storage_name_str);
651
652 let code = quote! {
654 #[allow(non_camel_case_types)]
655 #visibility struct #name< #impl_generics >(
656 ::core::marker::PhantomData<(#type_generics)>
657 ) #where_clause;
658
659 impl<#impl_generics> #crate_::traits::StorageInstance
660 for #name< #type_generics > #where_clause
661 {
662 fn pallet_prefix() -> &'static str {
663 #pallet_prefix
664 }
665
666 const STORAGE_PREFIX: &'static str = #storage_name_str;
667 fn storage_prefix_hash() -> [u8; 16] {
668 #storage_prefix_hash
669 }
670 }
671
672 #counter_code
673 };
674
675 Ok(StorageInstance { name, code, generics: quote!( < #type_generics > ) })
676}