-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Allow renaming storage item prefixes #9016
Changes from all commits
507c312
a1e75b2
c87e4d0
9ea8251
9d6a9e8
34fbf92
5277f7d
fc49baf
b8895a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,28 +25,60 @@ mod keyword { | |
| syn::custom_keyword!(Error); | ||
| syn::custom_keyword!(pallet); | ||
| syn::custom_keyword!(getter); | ||
| syn::custom_keyword!(storage_prefix); | ||
| syn::custom_keyword!(OptionQuery); | ||
| syn::custom_keyword!(ValueQuery); | ||
| } | ||
|
|
||
| /// Parse for `#[pallet::getter(fn dummy)]` | ||
| pub struct PalletStorageAttr { | ||
| getter: syn::Ident, | ||
| /// Parse for one of the following: | ||
| /// * `#[pallet::getter(fn dummy)]` | ||
| /// * `#[pallet::storage_prefix = "CustomName"]` | ||
| pub enum PalletStorageAttr { | ||
| Getter(syn::Ident, proc_macro2::Span), | ||
| StorageName(syn::LitStr, proc_macro2::Span), | ||
| } | ||
|
KiChjang marked this conversation as resolved.
|
||
|
|
||
| impl PalletStorageAttr { | ||
| fn attr_span(&self) -> proc_macro2::Span { | ||
| match self { | ||
| Self::Getter(_, span) | Self::StorageName(_, span) => *span, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl syn::parse::Parse for PalletStorageAttr { | ||
| fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { | ||
| input.parse::<syn::Token![#]>()?; | ||
| let attr_span = input.span(); | ||
| let content; | ||
| syn::bracketed!(content in input); | ||
| content.parse::<keyword::pallet>()?; | ||
| content.parse::<syn::Token![::]>()?; | ||
| content.parse::<keyword::getter>()?; | ||
|
|
||
| let generate_content; | ||
| syn::parenthesized!(generate_content in content); | ||
| generate_content.parse::<syn::Token![fn]>()?; | ||
| Ok(Self { getter: generate_content.parse::<syn::Ident>()? }) | ||
| let lookahead = content.lookahead1(); | ||
| if lookahead.peek(keyword::getter) { | ||
| content.parse::<keyword::getter>()?; | ||
|
|
||
| let generate_content; | ||
| syn::parenthesized!(generate_content in content); | ||
| generate_content.parse::<syn::Token![fn]>()?; | ||
| Ok(Self::Getter(generate_content.parse::<syn::Ident>()?, attr_span)) | ||
| } else if lookahead.peek(keyword::storage_prefix) { | ||
| content.parse::<keyword::storage_prefix>()?; | ||
| content.parse::<syn::Token![=]>()?; | ||
|
|
||
| let renamed_prefix = content.parse::<syn::LitStr>()?; | ||
| // Ensure the renamed prefix is a proper Rust identifier | ||
| syn::parse_str::<syn::Ident>(&renamed_prefix.value()) | ||
| .map_err(|_| { | ||
| let msg = format!("`{}` is not a valid identifier", renamed_prefix.value()); | ||
| syn::Error::new(renamed_prefix.span(), msg) | ||
| })?; | ||
|
|
||
| Ok(Self::StorageName(renamed_prefix, attr_span)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just a sidenote, now that we don't use renamed_prefix as ident, we don't need to force it to be a valid ident anymore AFAICT
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want that though? This would mean that it's possible to create storage prefixes that does not follow Rust identifier naming conventions. I guess that kind of makes sense, but something tells me that it's a bad idea, like there might be a footgun lying there somewhere if we're too permissive...
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, we can leverage the constraint when wanted. |
||
| } else { | ||
| Err(lookahead.error()) | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -89,6 +121,8 @@ pub struct StorageDef { | |
| pub instances: Vec<helper::InstanceUsage>, | ||
| /// Optional getter to generate. If some then query_kind is ensured to be some as well. | ||
| pub getter: Option<syn::Ident>, | ||
| /// Optional expression that evaluates to a type that can be used as StoragePrefix instead of ident. | ||
| pub rename_as: Option<syn::LitStr>, | ||
| /// Whereas the querytype of the storage is OptionQuery or ValueQuery. | ||
| /// Note that this is best effort as it can't be determined when QueryKind is generic, and | ||
| /// result can be false if user do some unexpected type alias. | ||
|
|
@@ -105,7 +139,6 @@ pub struct StorageDef { | |
| pub named_generics: Option<StorageGenerics>, | ||
| } | ||
|
|
||
|
|
||
| /// The parsed generic from the | ||
| #[derive(Clone)] | ||
| pub enum StorageGenerics { | ||
|
|
@@ -541,6 +574,25 @@ fn extract_key(ty: &syn::Type) -> syn::Result<syn::Type> { | |
| } | ||
|
|
||
| impl StorageDef { | ||
| /// Return the storage prefix for this storage item | ||
| pub fn prefix(&self) -> String { | ||
| self | ||
| .rename_as | ||
| .as_ref() | ||
| .map(syn::LitStr::value) | ||
| .unwrap_or(self.ident.to_string()) | ||
| } | ||
|
|
||
| /// Return either the span of the ident or the span of the literal in the | ||
| /// #[storage_prefix] attribute | ||
| pub fn prefix_span(&self) -> proc_macro2::Span { | ||
| self | ||
| .rename_as | ||
| .as_ref() | ||
| .map(syn::LitStr::span) | ||
| .unwrap_or(self.ident.span()) | ||
| } | ||
|
|
||
| pub fn try_from( | ||
| attr_span: proc_macro2::Span, | ||
| index: usize, | ||
|
|
@@ -552,12 +604,30 @@ impl StorageDef { | |
| return Err(syn::Error::new(item.span(), "Invalid pallet::storage, expect item type.")); | ||
| }; | ||
|
|
||
| let mut attrs: Vec<PalletStorageAttr> = helper::take_item_pallet_attrs(&mut item.attrs)?; | ||
| if attrs.len() > 1 { | ||
| let attrs: Vec<PalletStorageAttr> = helper::take_item_pallet_attrs(&mut item.attrs)?; | ||
| let (mut getters, mut names) = attrs | ||
| .into_iter() | ||
| .partition::<Vec<_>, _>(|attr| matches!(attr, PalletStorageAttr::Getter(..))); | ||
| if getters.len() > 1 { | ||
| let msg = "Invalid pallet::storage, multiple argument pallet::getter found"; | ||
| return Err(syn::Error::new(attrs[1].getter.span(), msg)); | ||
| return Err(syn::Error::new(getters[1].attr_span(), msg)); | ||
| } | ||
| let getter = attrs.pop().map(|attr| attr.getter); | ||
| if names.len() > 1 { | ||
| let msg = "Invalid pallet::storage, multiple argument pallet::storage_prefix found"; | ||
| return Err(syn::Error::new(names[1].attr_span(), msg)); | ||
| } | ||
| let getter = getters.pop().map(|attr| { | ||
| match attr { | ||
| PalletStorageAttr::Getter(ident, _) => ident, | ||
| _ => unreachable!(), | ||
| } | ||
| }); | ||
| let rename_as = names.pop().map(|attr| { | ||
| match attr { | ||
| PalletStorageAttr::StorageName(lit, _) => lit, | ||
| _ => unreachable!(), | ||
| } | ||
| }); | ||
|
|
||
| let cfg_attrs = helper::get_item_cfg_attrs(&item.attrs); | ||
|
|
||
|
|
@@ -609,6 +679,7 @@ impl StorageDef { | |
| metadata, | ||
| docs, | ||
| getter, | ||
| rename_as, | ||
| query_kind, | ||
| where_clause, | ||
| cfg_attrs, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -198,6 +198,10 @@ pub mod pallet { | |
| #[pallet::storage] | ||
| pub type Value<T> = StorageValue<Value = u32>; | ||
|
|
||
| #[pallet::storage] | ||
| #[pallet::storage_prefix = "Value2"] | ||
| pub type RenamedValue<T> = StorageValue<Value = u64>; | ||
|
|
||
| #[pallet::type_value] | ||
| pub fn MyDefault<T: Config>() -> u16 | ||
| where T::AccountId: From<SomeType7> + From<SomeType1> + SomeAssociation1 | ||
|
|
@@ -577,6 +581,10 @@ fn storage_expand() { | |
| let k = [twox_128(b"Example"), twox_128(b"Value")].concat(); | ||
| assert_eq!(unhashed::get::<u32>(&k), Some(1u32)); | ||
|
|
||
| pallet::RenamedValue::<Runtime>::put(2); | ||
| let k = [twox_128(b"Example"), twox_128(b"Value2")].concat(); | ||
| assert_eq!(unhashed::get::<u64>(&k), Some(2)); | ||
|
|
||
| pallet::Map::<Runtime>::insert(1, 2); | ||
| let mut k = [twox_128(b"Example"), twox_128(b"Map")].concat(); | ||
| k.extend(1u8.using_encoded(blake2_128_concat)); | ||
|
|
@@ -697,6 +705,13 @@ fn metadata() { | |
| default: DecodeDifferent::Decoded(vec![0]), | ||
| documentation: DecodeDifferent::Decoded(vec![]), | ||
| }, | ||
| StorageEntryMetadata { | ||
| name: DecodeDifferent::Decoded("Value2".to_string()), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we might actually want to break the metadata here... Maybe not right in this PR, but you can imagine someone will want to expose the name as "Balance" but use a custom prefix like "1". As such, there would be a But this is something that would be much more annoying to merge in, and so probably should be an issue and we save it for when we already will break metadata. |
||
| modifier: StorageEntryModifier::Optional, | ||
| ty: StorageEntryType::Plain(DecodeDifferent::Decoded("u64".to_string())), | ||
| default: DecodeDifferent::Decoded(vec![0]), | ||
| documentation: DecodeDifferent::Decoded(vec![]), | ||
| }, | ||
| StorageEntryMetadata { | ||
| name: DecodeDifferent::Decoded("Map".to_string()), | ||
| modifier: StorageEntryModifier::Default, | ||
|
|
@@ -993,6 +1008,11 @@ fn test_storage_info() { | |
| max_values: Some(1), | ||
| max_size: Some(4), | ||
| }, | ||
| StorageInfo { | ||
| prefix: prefix(b"Example", b"Value2"), | ||
| max_values: Some(1), | ||
| max_size: Some(8), | ||
| }, | ||
| StorageInfo { | ||
| prefix: prefix(b"Example", b"Map"), | ||
| max_values: None, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| #[frame_support::pallet] | ||
| mod pallet { | ||
| use frame_support::pallet_prelude::StorageValue; | ||
|
|
||
| #[pallet::config] | ||
| pub trait Config: frame_system::Config {} | ||
|
|
||
| #[pallet::pallet] | ||
| #[pallet::generate_store(trait Store)] | ||
| pub struct Pallet<T>(core::marker::PhantomData<T>); | ||
|
|
||
| #[pallet::storage] | ||
| type Foo<T> = StorageValue<_, u8>; | ||
|
|
||
| #[pallet::storage] | ||
| #[pallet::storage_prefix = "Foo"] | ||
| type NotFoo<T> = StorageValue<_, u16>; | ||
| } | ||
|
|
||
| fn main() { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| error: Duplicate storage prefixes found for `Foo` | ||
| --> $DIR/duplicate_storage_prefix.rs:16:32 | ||
| | | ||
| 16 | #[pallet::storage_prefix = "Foo"] | ||
| | ^^^^^ | ||
|
|
||
| error[E0412]: cannot find type `_GeneratedPrefixForStorageFoo` in this scope | ||
| --> $DIR/duplicate_storage_prefix.rs:13:7 | ||
| | | ||
| 13 | type Foo<T> = StorageValue<_, u8>; | ||
| | ^^^ not found in this scope | ||
|
|
||
| error[E0121]: the type placeholder `_` is not allowed within types on item signatures | ||
| --> $DIR/duplicate_storage_prefix.rs:17:35 | ||
| | | ||
| 17 | type NotFoo<T> = StorageValue<_, u16>; | ||
| | ^ not allowed in type signatures |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| #[frame_support::pallet] | ||
| mod pallet { | ||
| use frame_support::pallet_prelude::Hooks; | ||
| use frame_system::pallet_prelude::BlockNumberFor; | ||
|
|
||
| #[pallet::config] | ||
| pub trait Config: frame_system::Config {} | ||
|
|
||
| #[pallet::pallet] | ||
| pub struct Pallet<T>(_); | ||
|
|
||
| #[pallet::call] | ||
| impl<T: Config> Pallet<T> {} | ||
|
|
||
| #[pallet::storage] | ||
| #[pallet::generate_store(pub trait Store)] | ||
| type Foo<T> = StorageValue<u8, u8>; | ||
| } | ||
|
|
||
| fn main() { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| error: expected `getter` or `storage_prefix` | ||
| --> $DIR/storage_invalid_attribute.rs:16:12 | ||
| | | ||
| 16 | #[pallet::generate_store(pub trait Store)] | ||
| | ^^^^^^^^^^^^^^ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| #[frame_support::pallet] | ||
| mod pallet { | ||
| use frame_support::pallet_prelude::Hooks; | ||
| use frame_system::pallet_prelude::BlockNumberFor; | ||
|
|
||
| #[pallet::config] | ||
| pub trait Config: frame_system::Config {} | ||
|
|
||
| #[pallet::pallet] | ||
| pub struct Pallet<T>(core::marker::PhantomData<T>); | ||
|
|
||
| #[pallet::storage] | ||
| #[pallet::storage_prefix = "pub"] | ||
| type Foo<T> = StorageValue<_, u8>; | ||
| } | ||
|
|
||
| fn main() { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| error: `pub` is not a valid identifier | ||
| --> $DIR/storage_invalid_rename_value.rs:13:29 | ||
| | | ||
| 13 | #[pallet::storage_prefix = "pub"] | ||
| | ^^^^^ |
Uh oh!
There was an error while loading. Please reload this page.