Implement StorageNMap (#8635)

* Implement StorageNMap

* Change copyright date to 2021

* Rewrite keys to use impl_for_tuples instead of recursion

* Implement prefix iteration on StorageNMap

* Implement EncodeLike for key arguments

* Rename KeyGenerator::Arg to KeyGenerator::KArg

* Support StorageNMap in decl_storage and #[pallet::storage] macros

* Use StorageNMap in assets pallet

* Support migrate_keys in StorageNMap

* Reduce line characters on select files

* Refactor crate imports in decl_storage macros

* Some more line char reductions and doc comment update

* Update UI test expectations

* Revert whitespace changes to untouched files

* Generate Key struct instead of a 1-tuple when only 1 pair of key and hasher is provided

* Revert formatting changes to unrelated files

* Introduce KeyGeneratorInner

* Add tests for StorageNMap in FRAMEv2 pallet macro

* Small fixes to unit tests for StorageNMap

* Bump runtime metadata version

* Remove unused import

* Update tests to use runtime metadata v13

* Introduce and use EncodeLikeTuple as a trait bound for KArg

* Add some rustdocs

* Revert usage of StorageNMap in assets pallet

* Make use of ext::PunctuatedTrailing

* Add rustdoc for final_hash

* Fix StorageNMap proc macro expansions for single key cases

* Create associated const in KeyGenerator for hasher metadata

* Refactor code according to comments from Basti

* Add module docs for generator/nmap.rs

* Re-export storage::Key as NMapKey in pallet prelude

* Seal the EncodeLikeTuple trait

* Extract sealing code out of key.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Keith Yeung
2021-05-14 02:44:29 -07:00
committed by GitHub
parent c6b1240e51
commit 033d8289f0
26 changed files with 3210 additions and 50 deletions
@@ -50,7 +50,7 @@ impl syn::parse::Parse for PalletStorageAttr {
}
/// The value and key types used by storages. Needed to expand metadata.
pub enum Metadata{
pub enum Metadata {
Value { value: syn::GenericArgument },
Map { value: syn::GenericArgument, key: syn::GenericArgument },
DoubleMap {
@@ -58,6 +58,11 @@ pub enum Metadata{
key1: syn::GenericArgument,
key2: syn::GenericArgument
},
NMap {
keys: Vec<syn::Type>,
keygen: syn::GenericArgument,
value: syn::GenericArgument,
},
}
pub enum QueryKind {
@@ -115,6 +120,64 @@ fn retrieve_arg(
}
}
/// Parse the 2nd type argument to `StorageNMap` and return its keys.
fn collect_keys(keygen: &syn::GenericArgument) -> syn::Result<Vec<syn::Type>> {
if let syn::GenericArgument::Type(syn::Type::Tuple(tup)) = keygen {
tup
.elems
.iter()
.map(extract_key)
.collect::<syn::Result<Vec<_>>>()
} else if let syn::GenericArgument::Type(ty) = keygen {
Ok(vec![extract_key(ty)?])
} else {
let msg = format!("Invalid pallet::storage, expected tuple of Key structs or Key struct");
Err(syn::Error::new(keygen.span(), msg))
}
}
/// In `Key<H, K>`, extract K and return it.
fn extract_key(ty: &syn::Type) -> syn::Result<syn::Type> {
let typ = if let syn::Type::Path(typ) = ty {
typ
} else {
let msg = "Invalid pallet::storage, expected type path";
return Err(syn::Error::new(ty.span(), msg));
};
let key_struct = typ.path.segments.last().ok_or_else(|| {
let msg = "Invalid pallet::storage, expected type path with at least one segment";
syn::Error::new(typ.path.span(), msg)
})?;
if key_struct.ident != "Key" && key_struct.ident != "NMapKey" {
let msg = "Invalid pallet::storage, expected Key or NMapKey struct";
return Err(syn::Error::new(key_struct.ident.span(), msg));
}
let ty_params = if let syn::PathArguments::AngleBracketed(args) = &key_struct.arguments {
args
} else {
let msg = "Invalid pallet::storage, expected angle bracketed arguments";
return Err(syn::Error::new(key_struct.arguments.span(), msg));
};
if ty_params.args.len() != 2 {
let msg = format!("Invalid pallet::storage, unexpected number of generic arguments \
for Key struct, expected 2 args, found {}", ty_params.args.len());
return Err(syn::Error::new(ty_params.span(), msg));
}
let key = match &ty_params.args[1] {
syn::GenericArgument::Type(key_ty) => key_ty.clone(),
_ => {
let msg = "Invalid pallet::storage, expected type";
return Err(syn::Error::new(ty_params.args[1].span(), msg));
}
};
Ok(key)
}
impl StorageDef {
pub fn try_from(
attr_span: proc_macro2::Span,
@@ -177,11 +240,21 @@ impl StorageDef {
value: retrieve_arg(&typ.path.segments[0], 5)?,
}
}
"StorageNMap" => {
query_kind = retrieve_arg(&typ.path.segments[0], 3);
let keygen = retrieve_arg(&typ.path.segments[0], 1)?;
let keys = collect_keys(&keygen)?;
Metadata::NMap {
keys,
keygen,
value: retrieve_arg(&typ.path.segments[0], 2)?,
}
}
found => {
let msg = format!(
"Invalid pallet::storage, expected ident: `StorageValue` or \
`StorageMap` or `StorageDoubleMap` in order to expand metadata, found \
`{}`",
`StorageMap` or `StorageDoubleMap` or `StorageNMap` in order \
to expand metadata, found `{}`",
found,
);
return Err(syn::Error::new(item.ty.span(), msg));