Implement ResultQuery (#11257)

* Implement ResultQuery

* Fix test expectations

* Add more tests

* Fix test expectations

* Clean up some names

* Silence warnings

* Specify error type when supplying error type to ResultQuery

* cargo fmt

* Add support for type parameters in parameter_types macro

* Reduce deeply indented code

* Fixes

* Update test expectation

* Rewrite and document formula for calculating max storage size

* More docs

* cargo fmt

* formatting

Co-authored-by: parity-processbot <>
This commit is contained in:
Keith Yeung
2022-08-25 01:47:08 +08:00
committed by GitHub
parent 6aba2fdc99
commit edc8f7b409
16 changed files with 770 additions and 77 deletions
@@ -19,7 +19,9 @@ use crate::pallet::{
parse::storage::{Metadata, QueryKind, StorageDef, StorageGenerics},
Def,
};
use std::collections::HashMap;
use quote::ToTokens;
use std::{collections::HashMap, ops::IndexMut};
use syn::spanned::Spanned;
/// Generate the prefix_ident related to the storage.
/// prefix_ident is used for the prefix struct to be given to storage as first generic param.
@@ -84,12 +86,28 @@ fn check_prefix_duplicates(
Ok(())
}
pub struct ResultOnEmptyStructMetadata {
/// The Rust ident that is going to be used as the name of the OnEmpty struct.
pub name: syn::Ident,
/// The path to the error type being returned by the ResultQuery.
pub error_path: syn::Path,
/// The visibility of the OnEmpty struct.
pub visibility: syn::Visibility,
/// The type of the storage item.
pub value_ty: syn::Type,
/// The name of the pallet error enum variant that is going to be returned.
pub variant_name: syn::Ident,
/// The span used to report compilation errors about the OnEmpty struct.
pub span: proc_macro2::Span,
}
///
/// * if generics are unnamed: replace the first generic `_` by the generated prefix structure
/// * if generics are named: reorder the generic, remove their name, and add the missing ones.
/// * Add `#[allow(type_alias_bounds)]`
pub fn process_generics(def: &mut Def) -> syn::Result<()> {
pub fn process_generics(def: &mut Def) -> syn::Result<Vec<ResultOnEmptyStructMetadata>> {
let frame_support = &def.frame_support;
let mut on_empty_struct_metadata = Vec::new();
for storage_def in def.storages.iter_mut() {
let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index];
@@ -120,27 +138,72 @@ pub fn process_generics(def: &mut Def) -> syn::Result<()> {
let default_query_kind: syn::Type =
syn::parse_quote!(#frame_support::storage::types::OptionQuery);
let default_on_empty: syn::Type = syn::parse_quote!(#frame_support::traits::GetDefault);
let mut default_on_empty = |value_ty: syn::Type| -> syn::Type {
if let Some(QueryKind::ResultQuery(error_path, variant_name)) =
storage_def.query_kind.as_ref()
{
let on_empty_ident =
quote::format_ident!("__Frame_Internal_Get{}Result", storage_def.ident);
on_empty_struct_metadata.push(ResultOnEmptyStructMetadata {
name: on_empty_ident.clone(),
visibility: storage_def.vis.clone(),
value_ty,
error_path: error_path.clone(),
variant_name: variant_name.clone(),
span: storage_def.attr_span,
});
return syn::parse_quote!(#on_empty_ident)
}
syn::parse_quote!(#frame_support::traits::GetDefault)
};
let default_max_values: syn::Type = syn::parse_quote!(#frame_support::traits::GetDefault);
let set_result_query_type_parameter = |query_type: &mut syn::Type| -> syn::Result<()> {
if let Some(QueryKind::ResultQuery(error_path, _)) = storage_def.query_kind.as_ref() {
if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) =
query_type
{
if let Some(seg) = segments.last_mut() {
if let syn::PathArguments::AngleBracketed(
syn::AngleBracketedGenericArguments { args, .. },
) = &mut seg.arguments
{
args.clear();
args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path)));
}
}
} else {
let msg = format!(
"Invalid pallet::storage, unexpected type for query, expected ResultQuery \
with 1 type parameter, found `{}`",
query_type.to_token_stream().to_string()
);
return Err(syn::Error::new(query_type.span(), msg))
}
}
Ok(())
};
if let Some(named_generics) = storage_def.named_generics.clone() {
args.args.clear();
args.args.push(syn::parse_quote!( #prefix_ident<#type_use_gen> ));
match named_generics {
StorageGenerics::Value { value, query_kind, on_empty } => {
args.args.push(syn::GenericArgument::Type(value));
let query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
args.args.push(syn::GenericArgument::Type(value.clone()));
let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
set_result_query_type_parameter(&mut query_kind)?;
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty.clone());
let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
args.args.push(syn::GenericArgument::Type(on_empty));
},
StorageGenerics::Map { hasher, key, value, query_kind, on_empty, max_values } => {
args.args.push(syn::GenericArgument::Type(hasher));
args.args.push(syn::GenericArgument::Type(key));
args.args.push(syn::GenericArgument::Type(value));
let query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
args.args.push(syn::GenericArgument::Type(value.clone()));
let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
set_result_query_type_parameter(&mut query_kind)?;
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty.clone());
let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
args.args.push(syn::GenericArgument::Type(on_empty));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
@@ -155,10 +218,11 @@ pub fn process_generics(def: &mut Def) -> syn::Result<()> {
} => {
args.args.push(syn::GenericArgument::Type(hasher));
args.args.push(syn::GenericArgument::Type(key));
args.args.push(syn::GenericArgument::Type(value));
let query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
args.args.push(syn::GenericArgument::Type(value.clone()));
let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
set_result_query_type_parameter(&mut query_kind)?;
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty.clone());
let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
args.args.push(syn::GenericArgument::Type(on_empty));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
@@ -177,20 +241,22 @@ pub fn process_generics(def: &mut Def) -> syn::Result<()> {
args.args.push(syn::GenericArgument::Type(key1));
args.args.push(syn::GenericArgument::Type(hasher2));
args.args.push(syn::GenericArgument::Type(key2));
args.args.push(syn::GenericArgument::Type(value));
let query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
args.args.push(syn::GenericArgument::Type(value.clone()));
let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
set_result_query_type_parameter(&mut query_kind)?;
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty.clone());
let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
args.args.push(syn::GenericArgument::Type(on_empty));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
},
StorageGenerics::NMap { keygen, value, query_kind, on_empty, max_values } => {
args.args.push(syn::GenericArgument::Type(keygen));
args.args.push(syn::GenericArgument::Type(value));
let query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
args.args.push(syn::GenericArgument::Type(value.clone()));
let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
set_result_query_type_parameter(&mut query_kind)?;
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty.clone());
let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
args.args.push(syn::GenericArgument::Type(on_empty));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
@@ -198,10 +264,40 @@ pub fn process_generics(def: &mut Def) -> syn::Result<()> {
}
} else {
args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> );
let (value_idx, query_idx, on_empty_idx) = match storage_def.metadata {
Metadata::Value { .. } => (1, 2, 3),
Metadata::NMap { .. } => (2, 3, 4),
Metadata::Map { .. } | Metadata::CountedMap { .. } => (3, 4, 5),
Metadata::DoubleMap { .. } => (5, 6, 7),
};
if query_idx < args.args.len() {
if let syn::GenericArgument::Type(query_kind) = args.args.index_mut(query_idx) {
set_result_query_type_parameter(query_kind)?;
}
} else if let Some(QueryKind::ResultQuery(error_path, _)) =
storage_def.query_kind.as_ref()
{
args.args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path)))
}
// Here, we only need to check if OnEmpty is *not* specified, and if so, then we have to
// generate a default OnEmpty struct for it.
if on_empty_idx >= args.args.len() &&
matches!(storage_def.query_kind.as_ref(), Some(QueryKind::ResultQuery(_, _)))
{
let value_ty = match args.args[value_idx].clone() {
syn::GenericArgument::Type(ty) => ty,
_ => unreachable!(),
};
let on_empty = default_on_empty(value_ty);
args.args.push(syn::GenericArgument::Type(on_empty));
}
}
}
Ok(())
Ok(on_empty_struct_metadata)
}
///
@@ -212,9 +308,10 @@ pub fn process_generics(def: &mut Def) -> syn::Result<()> {
/// * Add `#[allow(type_alias_bounds)]` on storages type alias
/// * generate metadatas
pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
if let Err(e) = process_generics(def) {
return e.into_compile_error()
}
let on_empty_struct_metadata = match process_generics(def) {
Ok(idents) => idents,
Err(e) => return e.into_compile_error(),
};
// Check for duplicate prefixes
let mut prefix_set = HashMap::new();
@@ -277,6 +374,10 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
Option<#value>
),
QueryKind::ResultQuery(error_path, _) =>
quote::quote_spanned!(storage.attr_span =>
Result<#value, #error_path>
),
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(storage.attr_span =>
@@ -296,6 +397,10 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
Option<#value>
),
QueryKind::ResultQuery(error_path, _) =>
quote::quote_spanned!(storage.attr_span =>
Result<#value, #error_path>
),
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(storage.attr_span =>
@@ -317,6 +422,10 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
Option<#value>
),
QueryKind::ResultQuery(error_path, _) =>
quote::quote_spanned!(storage.attr_span =>
Result<#value, #error_path>
),
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(storage.attr_span =>
@@ -338,6 +447,10 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
Option<#value>
),
QueryKind::ResultQuery(error_path, _) =>
quote::quote_spanned!(storage.attr_span =>
Result<#value, #error_path>
),
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(storage.attr_span =>
@@ -361,6 +474,10 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
Option<#value>
),
QueryKind::ResultQuery(error_path, _) =>
quote::quote_spanned!(storage.attr_span =>
Result<#value, #error_path>
),
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(storage.attr_span =>
@@ -459,6 +576,61 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
)
});
let on_empty_structs = on_empty_struct_metadata.into_iter().map(|metadata| {
use crate::pallet::parse::GenericKind;
use syn::{GenericArgument, Path, PathArguments, PathSegment, Type, TypePath};
let ResultOnEmptyStructMetadata {
name,
visibility,
value_ty,
error_path,
variant_name,
span,
} = metadata;
let generic_kind = match error_path.segments.last() {
Some(PathSegment { arguments: PathArguments::AngleBracketed(args), .. }) => {
let (has_config, has_instance) =
args.args.iter().fold((false, false), |(has_config, has_instance), arg| {
match arg {
GenericArgument::Type(Type::Path(TypePath {
path: Path { segments, .. },
..
})) => {
let maybe_config =
segments.first().map_or(false, |seg| seg.ident == "T");
let maybe_instance =
segments.first().map_or(false, |seg| seg.ident == "I");
(has_config || maybe_config, has_instance || maybe_instance)
},
_ => (has_config, has_instance),
}
});
GenericKind::from_gens(has_config, has_instance).unwrap_or(GenericKind::None)
},
_ => GenericKind::None,
};
let type_impl_gen = generic_kind.type_impl_gen(proc_macro2::Span::call_site());
let config_where_clause = &def.config.where_clause;
quote::quote_spanned!(span =>
#[doc(hidden)]
#[allow(non_camel_case_types)]
#visibility struct #name;
impl<#type_impl_gen> #frame_support::traits::Get<Result<#value_ty, #error_path>>
for #name
#config_where_clause
{
fn get() -> Result<#value_ty, #error_path> {
Err(<#error_path>::#variant_name)
}
}
)
});
let mut where_clauses = vec![&def.config.where_clause];
where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause));
let completed_where_clause = super::merge_where_clauses(&where_clauses);
@@ -489,5 +661,6 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
#( #getters )*
#( #prefix_structs )*
#( #on_empty_structs )*
)
}
@@ -29,6 +29,7 @@ mod keyword {
syn::custom_keyword!(storage_prefix);
syn::custom_keyword!(unbounded);
syn::custom_keyword!(OptionQuery);
syn::custom_keyword!(ResultQuery);
syn::custom_keyword!(ValueQuery);
}
@@ -129,6 +130,7 @@ pub enum Metadata {
pub enum QueryKind {
OptionQuery,
ResultQuery(syn::Path, syn::Ident),
ValueQuery,
}
@@ -153,7 +155,7 @@ pub struct StorageDef {
/// 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.
/// Whereas the querytype of the storage is OptionQuery, ResultQuery 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.
pub query_kind: Option<QueryKind>,
@@ -695,21 +697,105 @@ impl StorageDef {
let (named_generics, metadata, query_kind) = process_generics(&typ.path.segments[0])?;
let query_kind = query_kind
.map(|query_kind| match query_kind {
syn::Type::Path(path)
if path.path.segments.last().map_or(false, |s| s.ident == "OptionQuery") =>
Some(QueryKind::OptionQuery),
syn::Type::Path(path)
if path.path.segments.last().map_or(false, |s| s.ident == "ValueQuery") =>
Some(QueryKind::ValueQuery),
_ => None,
.map(|query_kind| {
use syn::{
AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, Type,
TypePath,
};
let result_query = match query_kind {
Type::Path(path)
if path
.path
.segments
.last()
.map_or(false, |s| s.ident == "OptionQuery") =>
return Ok(Some(QueryKind::OptionQuery)),
Type::Path(TypePath { path: Path { segments, .. }, .. })
if segments.last().map_or(false, |s| s.ident == "ResultQuery") =>
segments
.last()
.expect("segments is checked to have the last value; qed")
.clone(),
Type::Path(path)
if path.path.segments.last().map_or(false, |s| s.ident == "ValueQuery") =>
return Ok(Some(QueryKind::ValueQuery)),
_ => return Ok(None),
};
let error_type = match result_query.arguments {
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
args, ..
}) => {
if args.len() != 1 {
let msg = format!(
"Invalid pallet::storage, unexpected number of generic arguments \
for ResultQuery, expected 1 type argument, found {}",
args.len(),
);
return Err(syn::Error::new(args.span(), msg))
}
args[0].clone()
},
args => {
let msg = format!(
"Invalid pallet::storage, unexpected generic args for ResultQuery, \
expected angle-bracketed arguments, found `{}`",
args.to_token_stream().to_string()
);
return Err(syn::Error::new(args.span(), msg))
},
};
match error_type {
GenericArgument::Type(Type::Path(TypePath {
path: Path { segments: err_variant, leading_colon },
..
})) => {
if err_variant.len() < 2 {
let msg = format!(
"Invalid pallet::storage, unexpected number of path segments for \
the generics in ResultQuery, expected a path with at least 2 \
segments, found {}",
err_variant.len(),
);
return Err(syn::Error::new(err_variant.span(), msg))
}
let mut error = err_variant.clone();
let err_variant = error
.pop()
.expect("Checked to have at least 2; qed")
.into_value()
.ident;
// Necessary here to eliminate the last double colon
let last =
error.pop().expect("Checked to have at least 2; qed").into_value();
error.push_value(last);
Ok(Some(QueryKind::ResultQuery(
syn::Path { leading_colon: leading_colon.clone(), segments: error },
err_variant,
)))
},
gen_arg => {
let msg = format!(
"Invalid pallet::storage, unexpected generic argument kind, expected a \
type path to a `PalletError` enum variant, found `{}`",
gen_arg.to_token_stream().to_string(),
);
Err(syn::Error::new(gen_arg.span(), msg))
},
}
})
.unwrap_or(Some(QueryKind::OptionQuery)); // This value must match the default generic.
.transpose()?
.unwrap_or(Some(QueryKind::OptionQuery));
if let (None, Some(getter)) = (query_kind.as_ref(), getter.as_ref()) {
let msg = "Invalid pallet::storage, cannot generate getter because QueryKind is not \
identifiable. QueryKind must be `OptionQuery`, `ValueQuery`, or default one to be \
identifiable.";
identifiable. QueryKind must be `OptionQuery`, `ResultQuery`, `ValueQuery`, or default \
one to be identifiable.";
return Err(syn::Error::new(getter.span(), msg))
}
+52 -29
View File
@@ -281,79 +281,85 @@ pub use frame_support_procedural::storage_alias;
macro_rules! parameter_types {
(
$( #[ $attr:meta ] )*
$vis:vis const $name:ident: $type:ty = $value:expr;
$vis:vis const $name:ident $(< $($ty_params:ident),* >)?: $type:ty = $value:expr;
$( $rest:tt )*
) => (
$( #[ $attr ] )*
$vis struct $name;
$crate::parameter_types!(IMPL_CONST $name , $type , $value);
$vis struct $name $(
< $($ty_params),* >( $($crate::sp_std::marker::PhantomData<$ty_params>),* )
)?;
$crate::parameter_types!(IMPL_CONST $name , $type , $value $( $(, $ty_params)* )?);
$crate::parameter_types!( $( $rest )* );
);
(
$( #[ $attr:meta ] )*
$vis:vis $name:ident: $type:ty = $value:expr;
$vis:vis $name:ident $(< $($ty_params:ident),* >)?: $type:ty = $value:expr;
$( $rest:tt )*
) => (
$( #[ $attr ] )*
$vis struct $name;
$crate::parameter_types!(IMPL $name, $type, $value);
$vis struct $name $(
< $($ty_params),* >( $($crate::sp_std::marker::PhantomData<$ty_params>),* )
)?;
$crate::parameter_types!(IMPL $name, $type, $value $( $(, $ty_params)* )?);
$crate::parameter_types!( $( $rest )* );
);
(
$( #[ $attr:meta ] )*
$vis:vis storage $name:ident: $type:ty = $value:expr;
$vis:vis storage $name:ident $(< $($ty_params:ident),* >)?: $type:ty = $value:expr;
$( $rest:tt )*
) => (
$( #[ $attr ] )*
$vis struct $name;
$crate::parameter_types!(IMPL_STORAGE $name, $type, $value);
$vis struct $name $(
< $($ty_params),* >( $($crate::sp_std::marker::PhantomData<$ty_params>),* )
)?;
$crate::parameter_types!(IMPL_STORAGE $name, $type, $value $( $(, $ty_params)* )?);
$crate::parameter_types!( $( $rest )* );
);
() => ();
(IMPL_CONST $name:ident, $type:ty, $value:expr) => {
impl $name {
(IMPL_CONST $name:ident, $type:ty, $value:expr $(, $ty_params:ident)*) => {
impl< $($ty_params),* > $name< $($ty_params),* > {
/// Returns the value of this parameter type.
pub const fn get() -> $type {
$value
}
}
impl<I: From<$type>> $crate::traits::Get<I> for $name {
fn get() -> I {
I::from(Self::get())
impl<_I: From<$type> $(, $ty_params)*> $crate::traits::Get<_I> for $name< $($ty_params),* > {
fn get() -> _I {
_I::from(Self::get())
}
}
impl $crate::traits::TypedGet for $name {
impl< $($ty_params),* > $crate::traits::TypedGet for $name< $($ty_params),* > {
type Type = $type;
fn get() -> $type {
Self::get()
}
}
};
(IMPL $name:ident, $type:ty, $value:expr) => {
impl $name {
(IMPL $name:ident, $type:ty, $value:expr $(, $ty_params:ident)*) => {
impl< $($ty_params),* > $name< $($ty_params),* > {
/// Returns the value of this parameter type.
pub fn get() -> $type {
$value
}
}
impl<I: From<$type>> $crate::traits::Get<I> for $name {
fn get() -> I {
I::from(Self::get())
impl<_I: From<$type>, $(, $ty_params)*> $crate::traits::Get<_I> for $name< $($ty_params),* > {
fn get() -> _I {
_I::from(Self::get())
}
}
impl $crate::traits::TypedGet for $name {
impl< $($ty_params),* > $crate::traits::TypedGet for $name< $($ty_params),* > {
type Type = $type;
fn get() -> $type {
Self::get()
}
}
};
(IMPL_STORAGE $name:ident, $type:ty, $value:expr) => {
impl $name {
(IMPL_STORAGE $name:ident, $type:ty, $value:expr $(, $ty_params:ident)*) => {
impl< $($ty_params),* > $name< $($ty_params),* > {
/// Returns the key for this parameter type.
#[allow(unused)]
pub fn key() -> [u8; 16] {
@@ -379,13 +385,13 @@ macro_rules! parameter_types {
}
}
impl<I: From<$type>> $crate::traits::Get<I> for $name {
fn get() -> I {
I::from(Self::get())
impl<_I: From<$type> $(, $ty_params)*> $crate::traits::Get<_I> for $name< $($ty_params),* > {
fn get() -> _I {
_I::from(Self::get())
}
}
impl $crate::traits::TypedGet for $name {
impl< $($ty_params),* > $crate::traits::TypedGet for $name< $($ty_params),* > {
type Type = $type;
fn get() -> $type {
Self::get()
@@ -1360,8 +1366,8 @@ pub mod pallet_prelude {
storage::{
bounded_vec::BoundedVec,
types::{
CountedStorageMap, Key as NMapKey, OptionQuery, StorageDoubleMap, StorageMap,
StorageNMap, StorageValue, ValueQuery,
CountedStorageMap, Key as NMapKey, OptionQuery, ResultQuery, StorageDoubleMap,
StorageMap, StorageNMap, StorageValue, ValueQuery,
},
},
traits::{
@@ -1835,6 +1841,23 @@ pub mod pallet_prelude {
/// All the `cfg` attributes are automatically copied to the items generated for the storage,
/// i.e. the getter, storage prefix, and the metadata element etc.
///
/// Any type placed as the `QueryKind` parameter must implement
/// [`frame_support::storage::types::QueryKindTrait`]. There are 3 implementations of this
/// trait by default:
/// 1. [`frame_support::storage::types::OptionQuery`], the default `QueryKind` used when this
/// type parameter is omitted. Specifying this as the `QueryKind` would cause storage map
/// APIs that return a `QueryKind` to instead return an `Option`, returning `Some` when a
/// value does exist under a specified storage key, and `None` otherwise.
/// 2. [`frame_support::storage::types::ValueQuery`] causes storage map APIs that return a
/// `QueryKind` to instead return the value type. In cases where a value does not exist
/// under a specified storage key, the `OnEmpty` type parameter on `QueryKindTrait` is used
/// to return an appropriate value.
/// 3. [`frame_support::storage::types::ResultQuery`] causes storage map APIs that return a
/// `QueryKind` to instead return a `Result<T, E>`, with `T` being the value type and `E`
/// being the pallet error type specified by the `#[pallet::error]` attribute. In cases
/// where a value does not exist under a specified storage key, an `Err` with the specified
/// pallet error variant is returned.
///
/// NOTE: If the `QueryKind` generic parameter is still generic at this stage or is using some
/// type alias then the generation of the getter might fail. In this case the getter can be
/// implemented manually.
@@ -42,9 +42,11 @@ pub use value::StorageValue;
/// Trait implementing how the storage optional value is converted into the queried type.
///
/// It is implemented by:
/// * `OptionQuery` which convert an optional value to an optional value, user when querying storage
/// will get an optional value.
/// * `ValueQuery` which convert an optional value to a value, user when querying storage will get a
/// * `OptionQuery` which converts an optional value to an optional value, used when querying
/// storage returns an optional value.
/// * `ResultQuery` which converts an optional value to a result value, used when querying storage
/// returns a result value.
/// * `ValueQuery` which converts an optional value to a value, used when querying storage returns a
/// value.
pub trait QueryKindTrait<Value, OnEmpty> {
/// Metadata for the storage kind.
@@ -85,6 +87,30 @@ where
}
}
/// Implement QueryKindTrait with query being `Result<Value, PalletError>`
pub struct ResultQuery<Error>(sp_std::marker::PhantomData<Error>);
impl<Value, Error, OnEmpty> QueryKindTrait<Value, OnEmpty> for ResultQuery<Error>
where
Value: FullCodec + 'static,
Error: FullCodec + 'static,
OnEmpty: crate::traits::Get<Result<Value, Error>>,
{
const METADATA: StorageEntryModifier = StorageEntryModifier::Optional;
type Query = Result<Value, Error>;
fn from_optional_value_to_query(v: Option<Value>) -> Self::Query {
match v {
Some(v) => Ok(v),
None => OnEmpty::get(),
}
}
fn from_query_to_optional_value(v: Self::Query) -> Option<Value> {
v.ok()
}
}
/// Implement QueryKindTrait with query being `Value`
pub struct ValueQuery;
impl<Value, OnEmpty> QueryKindTrait<Value, OnEmpty> for ValueQuery
+129 -10
View File
@@ -229,6 +229,7 @@ pub mod pallet {
pub enum Error<T> {
/// doc comment put into metadata
InsufficientProposersBalance,
NonExistentStorageValue,
Code(u8),
#[codec(skip)]
Skipped(u128),
@@ -282,6 +283,10 @@ pub mod pallet {
pub type Map2<T> =
StorageMap<Hasher = Twox64Concat, Key = u16, Value = u32, MaxValues = ConstU32<3>>;
#[pallet::storage]
pub type Map3<T> =
StorageMap<_, Blake2_128Concat, u32, u64, ResultQuery<Error<T>::NonExistentStorageValue>>;
#[pallet::storage]
pub type DoubleMap<T> = StorageDoubleMap<_, Blake2_128Concat, u8, Twox64Concat, u16, u32>;
@@ -295,6 +300,17 @@ pub mod pallet {
MaxValues = ConstU32<5>,
>;
#[pallet::storage]
pub type DoubleMap3<T> = StorageDoubleMap<
_,
Blake2_128Concat,
u32,
Twox64Concat,
u64,
u128,
ResultQuery<Error<T>::NonExistentStorageValue>,
>;
#[pallet::storage]
#[pallet::getter(fn nmap)]
pub type NMap<T> = StorageNMap<_, storage::Key<Blake2_128Concat, u8>, u32>;
@@ -307,6 +323,15 @@ pub mod pallet {
MaxValues = ConstU32<11>,
>;
#[pallet::storage]
#[pallet::getter(fn nmap3)]
pub type NMap3<T> = StorageNMap<
_,
(NMapKey<Blake2_128Concat, u8>, NMapKey<Twox64Concat, u16>),
u128,
ResultQuery<Error<T>::NonExistentStorageValue>,
>;
#[pallet::storage]
#[pallet::getter(fn conditional_value)]
#[cfg(feature = "conditional-storage")]
@@ -934,6 +959,16 @@ fn storage_expand() {
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
assert_eq!(&k[..32], &<pallet::Map2<Runtime>>::final_prefix());
pallet::Map3::<Runtime>::insert(1, 2);
let mut k = [twox_128(b"Example"), twox_128(b"Map3")].concat();
k.extend(1u32.using_encoded(blake2_128_concat));
assert_eq!(unhashed::get::<u64>(&k), Some(2u64));
assert_eq!(&k[..32], &<pallet::Map3<Runtime>>::final_prefix());
assert_eq!(
pallet::Map3::<Runtime>::get(2),
Err(pallet::Error::<Runtime>::NonExistentStorageValue),
);
pallet::DoubleMap::<Runtime>::insert(&1, &2, &3);
let mut k = [twox_128(b"Example"), twox_128(b"DoubleMap")].concat();
k.extend(1u8.using_encoded(blake2_128_concat));
@@ -948,6 +983,17 @@ fn storage_expand() {
assert_eq!(unhashed::get::<u64>(&k), Some(3u64));
assert_eq!(&k[..32], &<pallet::DoubleMap2<Runtime>>::final_prefix());
pallet::DoubleMap3::<Runtime>::insert(&1, &2, &3);
let mut k = [twox_128(b"Example"), twox_128(b"DoubleMap3")].concat();
k.extend(1u32.using_encoded(blake2_128_concat));
k.extend(2u64.using_encoded(twox_64_concat));
assert_eq!(unhashed::get::<u128>(&k), Some(3u128));
assert_eq!(&k[..32], &<pallet::DoubleMap3<Runtime>>::final_prefix());
assert_eq!(
pallet::DoubleMap3::<Runtime>::get(2, 3),
Err(pallet::Error::<Runtime>::NonExistentStorageValue),
);
pallet::NMap::<Runtime>::insert((&1,), &3);
let mut k = [twox_128(b"Example"), twox_128(b"NMap")].concat();
k.extend(1u8.using_encoded(blake2_128_concat));
@@ -961,6 +1007,17 @@ fn storage_expand() {
assert_eq!(unhashed::get::<u64>(&k), Some(3u64));
assert_eq!(&k[..32], &<pallet::NMap2<Runtime>>::final_prefix());
pallet::NMap3::<Runtime>::insert((&1, &2), &3);
let mut k = [twox_128(b"Example"), twox_128(b"NMap3")].concat();
k.extend(1u8.using_encoded(blake2_128_concat));
k.extend(2u16.using_encoded(twox_64_concat));
assert_eq!(unhashed::get::<u128>(&k), Some(3u128));
assert_eq!(&k[..32], &<pallet::NMap3<Runtime>>::final_prefix());
assert_eq!(
pallet::NMap3::<Runtime>::get((2, 3)),
Err(pallet::Error::<Runtime>::NonExistentStorageValue),
);
#[cfg(feature = "conditional-storage")]
{
pallet::ConditionalValue::<Runtime>::put(1);
@@ -1171,6 +1228,17 @@ fn metadata() {
default: vec![0],
docs: vec![],
},
StorageEntryMetadata {
name: "Map3",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Map {
key: meta_type::<u32>(),
value: meta_type::<u64>(),
hashers: vec![StorageHasher::Blake2_128Concat],
},
default: vec![1, 1],
docs: vec![],
},
StorageEntryMetadata {
name: "DoubleMap",
modifier: StorageEntryModifier::Optional,
@@ -1199,6 +1267,20 @@ fn metadata() {
default: vec![0],
docs: vec![],
},
StorageEntryMetadata {
name: "DoubleMap3",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Map {
value: meta_type::<u128>(),
key: meta_type::<(u32, u64)>(),
hashers: vec![
StorageHasher::Blake2_128Concat,
StorageHasher::Twox64Concat,
],
},
default: vec![1, 1],
docs: vec![],
},
StorageEntryMetadata {
name: "NMap",
modifier: StorageEntryModifier::Optional,
@@ -1224,6 +1306,20 @@ fn metadata() {
default: vec![0],
docs: vec![],
},
StorageEntryMetadata {
name: "NMap3",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Map {
key: meta_type::<(u8, u16)>(),
hashers: vec![
StorageHasher::Blake2_128Concat,
StorageHasher::Twox64Concat,
],
value: meta_type::<u128>(),
},
default: vec![1, 1],
docs: vec![],
},
#[cfg(feature = "conditional-storage")]
StorageEntryMetadata {
name: "ConditionalValue",
@@ -1436,6 +1532,8 @@ fn test_storage_info() {
traits::{StorageInfo, StorageInfoTrait},
};
// Storage max size is calculated by adding up all the hasher size, the key type size and the
// value type size
assert_eq!(
Example::storage_info(),
vec![
@@ -1465,42 +1563,63 @@ fn test_storage_info() {
storage_name: b"Map".to_vec(),
prefix: prefix(b"Example", b"Map").to_vec(),
max_values: None,
max_size: Some(3 + 16),
max_size: Some(16 + 1 + 2),
},
StorageInfo {
pallet_name: b"Example".to_vec(),
storage_name: b"Map2".to_vec(),
prefix: prefix(b"Example", b"Map2").to_vec(),
max_values: Some(3),
max_size: Some(6 + 8),
max_size: Some(8 + 2 + 4),
},
StorageInfo {
pallet_name: b"Example".to_vec(),
storage_name: b"Map3".to_vec(),
prefix: prefix(b"Example", b"Map3").to_vec(),
max_values: None,
max_size: Some(16 + 4 + 8),
},
StorageInfo {
pallet_name: b"Example".to_vec(),
storage_name: b"DoubleMap".to_vec(),
prefix: prefix(b"Example", b"DoubleMap").to_vec(),
max_values: None,
max_size: Some(7 + 16 + 8),
max_size: Some(16 + 1 + 8 + 2 + 4),
},
StorageInfo {
pallet_name: b"Example".to_vec(),
storage_name: b"DoubleMap2".to_vec(),
prefix: prefix(b"Example", b"DoubleMap2").to_vec(),
max_values: Some(5),
max_size: Some(14 + 8 + 16),
max_size: Some(8 + 2 + 16 + 4 + 8),
},
StorageInfo {
pallet_name: b"Example".to_vec(),
storage_name: b"DoubleMap3".to_vec(),
prefix: prefix(b"Example", b"DoubleMap3").to_vec(),
max_values: None,
max_size: Some(16 + 4 + 8 + 8 + 16),
},
StorageInfo {
pallet_name: b"Example".to_vec(),
storage_name: b"NMap".to_vec(),
prefix: prefix(b"Example", b"NMap").to_vec(),
max_values: None,
max_size: Some(5 + 16),
max_size: Some(16 + 1 + 4),
},
StorageInfo {
pallet_name: b"Example".to_vec(),
storage_name: b"NMap2".to_vec(),
prefix: prefix(b"Example", b"NMap2").to_vec(),
max_values: Some(11),
max_size: Some(14 + 8 + 16),
max_size: Some(8 + 2 + 16 + 4 + 8),
},
StorageInfo {
pallet_name: b"Example".to_vec(),
storage_name: b"NMap3".to_vec(),
prefix: prefix(b"Example", b"NMap3").to_vec(),
max_values: None,
max_size: Some(16 + 1 + 8 + 2 + 16),
},
#[cfg(feature = "conditional-storage")]
{
@@ -1519,7 +1638,7 @@ fn test_storage_info() {
storage_name: b"ConditionalMap".to_vec(),
prefix: prefix(b"Example", b"ConditionalMap").to_vec(),
max_values: Some(12),
max_size: Some(6 + 8),
max_size: Some(8 + 2 + 4),
}
},
#[cfg(feature = "conditional-storage")]
@@ -1529,7 +1648,7 @@ fn test_storage_info() {
storage_name: b"ConditionalDoubleMap".to_vec(),
prefix: prefix(b"Example", b"ConditionalDoubleMap").to_vec(),
max_values: None,
max_size: Some(7 + 16 + 8),
max_size: Some(16 + 1 + 8 + 2 + 4),
}
},
#[cfg(feature = "conditional-storage")]
@@ -1539,7 +1658,7 @@ fn test_storage_info() {
storage_name: b"ConditionalNMap".to_vec(),
prefix: prefix(b"Example", b"ConditionalNMap").to_vec(),
max_values: None,
max_size: Some(7 + 16 + 8),
max_size: Some(16 + 1 + 8 + 2 + 4),
}
},
StorageInfo {
@@ -1547,7 +1666,7 @@ fn test_storage_info() {
storage_name: b"RenamedCountedMap".to_vec(),
prefix: prefix(b"Example", b"RenamedCountedMap").to_vec(),
max_values: None,
max_size: Some(1 + 4 + 8),
max_size: Some(8 + 1 + 4),
},
StorageInfo {
pallet_name: b"Example".to_vec(),
@@ -31,7 +31,7 @@ use sp_runtime::{DispatchError, ModuleError};
#[frame_support::pallet]
pub mod pallet {
use codec::MaxEncodedLen;
use frame_support::{pallet_prelude::*, scale_info};
use frame_support::{pallet_prelude::*, parameter_types, scale_info};
use frame_system::pallet_prelude::*;
use sp_std::any::TypeId;
@@ -104,9 +104,11 @@ pub mod pallet {
}
#[pallet::error]
#[derive(PartialEq, Eq)]
pub enum Error<T, I = ()> {
/// doc comment put into metadata
InsufficientProposersBalance,
NonExistentStorageValue,
}
#[pallet::event]
@@ -128,6 +130,20 @@ pub mod pallet {
#[pallet::storage]
pub type Map2<T, I = ()> = StorageMap<_, Twox64Concat, u16, u32>;
parameter_types! {
pub const Map3Default<T, I>: Result<u64, Error<T, I>> = Ok(1337);
}
#[pallet::storage]
pub type Map3<T, I = ()> = StorageMap<
_,
Blake2_128Concat,
u32,
u64,
ResultQuery<Error<T, I>::NonExistentStorageValue>,
Map3Default<T, I>,
>;
#[pallet::storage]
pub type DoubleMap<T, I = ()> =
StorageDoubleMap<_, Blake2_128Concat, u8, Twox64Concat, u16, u32>;
@@ -136,6 +152,17 @@ pub mod pallet {
pub type DoubleMap2<T, I = ()> =
StorageDoubleMap<_, Twox64Concat, u16, Blake2_128Concat, u32, u64>;
#[pallet::storage]
pub type DoubleMap3<T, I = ()> = StorageDoubleMap<
_,
Blake2_128Concat,
u32,
Twox64Concat,
u64,
u128,
ResultQuery<Error<T, I>::NonExistentStorageValue>,
>;
#[pallet::storage]
#[pallet::getter(fn nmap)]
pub type NMap<T, I = ()> = StorageNMap<_, storage::Key<Blake2_128Concat, u8>, u32>;
@@ -145,6 +172,15 @@ pub mod pallet {
pub type NMap2<T, I = ()> =
StorageNMap<_, (storage::Key<Twox64Concat, u16>, storage::Key<Blake2_128Concat, u32>), u64>;
#[pallet::storage]
#[pallet::getter(fn nmap3)]
pub type NMap3<T, I = ()> = StorageNMap<
_,
(NMapKey<Blake2_128Concat, u8>, NMapKey<Twox64Concat, u16>),
u128,
ResultQuery<Error<T, I>::NonExistentStorageValue>,
>;
#[pallet::genesis_config]
#[derive(Default)]
pub struct GenesisConfig {
@@ -436,6 +472,13 @@ fn storage_expand() {
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
assert_eq!(&k[..32], &<pallet::Map2<Runtime>>::final_prefix());
<pallet::Map3<Runtime>>::insert(1, 2);
let mut k = [twox_128(b"Example"), twox_128(b"Map3")].concat();
k.extend(1u32.using_encoded(blake2_128_concat));
assert_eq!(unhashed::get::<u64>(&k), Some(2u64));
assert_eq!(&k[..32], &<pallet::Map3<Runtime>>::final_prefix());
assert_eq!(<pallet::Map3<Runtime>>::get(2), Ok(1337));
<pallet::DoubleMap<Runtime>>::insert(&1, &2, &3);
let mut k = [twox_128(b"Example"), twox_128(b"DoubleMap")].concat();
k.extend(1u8.using_encoded(blake2_128_concat));
@@ -450,6 +493,17 @@ fn storage_expand() {
assert_eq!(unhashed::get::<u64>(&k), Some(3u64));
assert_eq!(&k[..32], &<pallet::DoubleMap2<Runtime>>::final_prefix());
<pallet::DoubleMap3<Runtime>>::insert(&1, &2, &3);
let mut k = [twox_128(b"Example"), twox_128(b"DoubleMap3")].concat();
k.extend(1u32.using_encoded(blake2_128_concat));
k.extend(2u64.using_encoded(twox_64_concat));
assert_eq!(unhashed::get::<u128>(&k), Some(3u128));
assert_eq!(&k[..32], &<pallet::DoubleMap3<Runtime>>::final_prefix());
assert_eq!(
<pallet::DoubleMap3<Runtime>>::get(2, 3),
Err(pallet::Error::<Runtime>::NonExistentStorageValue),
);
<pallet::NMap<Runtime>>::insert((&1,), &3);
let mut k = [twox_128(b"Example"), twox_128(b"NMap")].concat();
k.extend(1u8.using_encoded(blake2_128_concat));
@@ -462,6 +516,17 @@ fn storage_expand() {
k.extend(2u32.using_encoded(blake2_128_concat));
assert_eq!(unhashed::get::<u64>(&k), Some(3u64));
assert_eq!(&k[..32], &<pallet::NMap2<Runtime>>::final_prefix());
<pallet::NMap3<Runtime>>::insert((&1, &2), &3);
let mut k = [twox_128(b"Example"), twox_128(b"NMap3")].concat();
k.extend(1u8.using_encoded(blake2_128_concat));
k.extend(2u16.using_encoded(twox_64_concat));
assert_eq!(unhashed::get::<u128>(&k), Some(3u128));
assert_eq!(&k[..32], &<pallet::NMap3<Runtime>>::final_prefix());
assert_eq!(
<pallet::NMap3<Runtime>>::get((2, 3)),
Err(pallet::Error::<Runtime>::NonExistentStorageValue),
);
});
TestExternalities::default().execute_with(|| {
@@ -481,6 +546,13 @@ fn storage_expand() {
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
assert_eq!(&k[..32], &<pallet::Map2<Runtime, pallet::Instance1>>::final_prefix());
<pallet::Map3<Runtime, pallet::Instance1>>::insert(1, 2);
let mut k = [twox_128(b"Instance1Example"), twox_128(b"Map3")].concat();
k.extend(1u32.using_encoded(blake2_128_concat));
assert_eq!(unhashed::get::<u64>(&k), Some(2u64));
assert_eq!(&k[..32], &<pallet::Map3<Runtime, pallet::Instance1>>::final_prefix());
assert_eq!(<pallet::Map3<Runtime, pallet::Instance1>>::get(2), Ok(1337));
<pallet::DoubleMap<Runtime, pallet::Instance1>>::insert(&1, &2, &3);
let mut k = [twox_128(b"Instance1Example"), twox_128(b"DoubleMap")].concat();
k.extend(1u8.using_encoded(blake2_128_concat));
@@ -495,6 +567,17 @@ fn storage_expand() {
assert_eq!(unhashed::get::<u64>(&k), Some(3u64));
assert_eq!(&k[..32], &<pallet::DoubleMap2<Runtime, pallet::Instance1>>::final_prefix());
<pallet::DoubleMap3<Runtime, pallet::Instance1>>::insert(&1, &2, &3);
let mut k = [twox_128(b"Instance1Example"), twox_128(b"DoubleMap3")].concat();
k.extend(1u32.using_encoded(blake2_128_concat));
k.extend(2u64.using_encoded(twox_64_concat));
assert_eq!(unhashed::get::<u128>(&k), Some(3u128));
assert_eq!(&k[..32], &<pallet::DoubleMap3<Runtime, pallet::Instance1>>::final_prefix());
assert_eq!(
<pallet::DoubleMap3<Runtime, pallet::Instance1>>::get(2, 3),
Err(pallet::Error::<Runtime, pallet::Instance1>::NonExistentStorageValue),
);
<pallet::NMap<Runtime, pallet::Instance1>>::insert((&1,), &3);
let mut k = [twox_128(b"Instance1Example"), twox_128(b"NMap")].concat();
k.extend(1u8.using_encoded(blake2_128_concat));
@@ -507,6 +590,17 @@ fn storage_expand() {
k.extend(2u32.using_encoded(blake2_128_concat));
assert_eq!(unhashed::get::<u64>(&k), Some(3u64));
assert_eq!(&k[..32], &<pallet::NMap2<Runtime, pallet::Instance1>>::final_prefix());
<pallet::NMap3<Runtime, pallet::Instance1>>::insert((&1, &2), &3);
let mut k = [twox_128(b"Instance1Example"), twox_128(b"NMap3")].concat();
k.extend(1u8.using_encoded(blake2_128_concat));
k.extend(2u16.using_encoded(twox_64_concat));
assert_eq!(unhashed::get::<u128>(&k), Some(3u128));
assert_eq!(&k[..32], &<pallet::NMap3<Runtime, pallet::Instance1>>::final_prefix());
assert_eq!(
<pallet::NMap3<Runtime, pallet::Instance1>>::get((2, 3)),
Err(pallet::Error::<Runtime, pallet::Instance1>::NonExistentStorageValue),
);
});
}
@@ -688,6 +782,17 @@ fn metadata() {
default: vec![0],
docs: vec![],
},
StorageEntryMetadata {
name: "Map3",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Map {
key: scale_info::meta_type::<u32>(),
value: scale_info::meta_type::<u64>(),
hashers: vec![StorageHasher::Blake2_128Concat],
},
default: vec![0, 57, 5, 0, 0, 0, 0, 0, 0],
docs: vec![],
},
StorageEntryMetadata {
name: "DoubleMap",
modifier: StorageEntryModifier::Optional,
@@ -710,6 +815,17 @@ fn metadata() {
default: vec![0],
docs: vec![],
},
StorageEntryMetadata {
name: "DoubleMap3",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Map {
value: scale_info::meta_type::<u128>(),
key: scale_info::meta_type::<(u32, u64)>(),
hashers: vec![StorageHasher::Blake2_128Concat, StorageHasher::Twox64Concat],
},
default: vec![1, 1],
docs: vec![],
},
StorageEntryMetadata {
name: "NMap",
modifier: StorageEntryModifier::Optional,
@@ -732,6 +848,17 @@ fn metadata() {
default: vec![0],
docs: vec![],
},
StorageEntryMetadata {
name: "NMap3",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Map {
key: scale_info::meta_type::<(u8, u16)>(),
hashers: vec![StorageHasher::Blake2_128Concat, StorageHasher::Twox64Concat],
value: scale_info::meta_type::<u128>(),
},
default: vec![1, 1],
docs: vec![],
},
],
}),
calls: Some(scale_info::meta_type::<pallet::Call<Runtime>>().into()),
@@ -0,0 +1,21 @@
#[frame_support::pallet]
mod pallet {
use frame_support::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(core::marker::PhantomData<T>);
#[pallet::error]
pub enum Error<T> {
NonExistentValue,
}
#[pallet::storage]
type Foo<T: Config> = StorageValue<_, u8, ResultQuery<Error::NonExistentValue>>;
}
fn main() {
}
@@ -0,0 +1,15 @@
error[E0107]: missing generics for enum `pallet::Error`
--> tests/pallet_ui/storage_result_query_missing_generics.rs:17:56
|
17 | type Foo<T: Config> = StorageValue<_, u8, ResultQuery<Error::NonExistentValue>>;
| ^^^^^ expected 1 generic argument
|
note: enum defined here, with 1 generic parameter: `T`
--> tests/pallet_ui/storage_result_query_missing_generics.rs:12:11
|
12 | pub enum Error<T> {
| ^^^^^ -
help: add missing generic argument
|
17 | type Foo<T: Config> = StorageValue<_, u8, ResultQuery<Error<T>::NonExistentValue>>;
| ~~~~~~~~
@@ -0,0 +1,23 @@
#[frame_support::pallet]
mod pallet {
use frame_support::pallet_prelude::*;
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::error]
pub enum Error<T> {
NonExistentValue,
SomeOtherError,
}
#[pallet::storage]
type Foo<T: Config> = StorageValue<_, u8, ResultQuery<Error<T>::NonExistentValue, SomeOtherError>>;
}
fn main() {
}
@@ -0,0 +1,5 @@
error: Invalid pallet::storage, unexpected number of generic arguments for ResultQuery, expected 1 type argument, found 2
--> tests/pallet_ui/storage_result_query_multiple_type_args.rs:19:56
|
19 | type Foo<T: Config> = StorageValue<_, u8, ResultQuery<Error<T>::NonExistentValue, SomeOtherError>>;
| ^^^^^
@@ -0,0 +1,16 @@
#[frame_support::pallet]
mod pallet {
use frame_support::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(core::marker::PhantomData<T>);
#[pallet::storage]
type Foo<T: Config> = StorageValue<_, u8, ResultQuery<NonExistentValue>>;
}
fn main() {
}
@@ -0,0 +1,5 @@
error: Invalid pallet::storage, unexpected number of path segments for the generics in ResultQuery, expected a path with at least 2 segments, found 1
--> tests/pallet_ui/storage_result_query_no_defined_pallet_error.rs:12:56
|
12 | type Foo<T: Config> = StorageValue<_, u8, ResultQuery<NonExistentValue>>;
| ^^^^^^^^^^^^^^^^
@@ -0,0 +1,22 @@
#[frame_support::pallet]
mod pallet {
use frame_support::pallet_prelude::*;
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::error]
pub enum Error<T> {
NonExistentValue,
}
#[pallet::storage]
type Foo<T: Config> = StorageValue<_, u8, ResultQuery(NonExistentValue)>;
}
fn main() {
}
@@ -0,0 +1,5 @@
error: Invalid pallet::storage, unexpected generic args for ResultQuery, expected angle-bracketed arguments, found `(NonExistentValue)`
--> tests/pallet_ui/storage_result_query_parenthesized_generics.rs:18:55
|
18 | type Foo<T: Config> = StorageValue<_, u8, ResultQuery(NonExistentValue)>;
| ^^^^^^^^^^^^^^^^^^
@@ -0,0 +1,22 @@
#[frame_support::pallet]
mod pallet {
use frame_support::pallet_prelude::*;
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::error]
pub enum Error<T> {
NonExistentValue,
}
#[pallet::storage]
type Foo<T: Config> = StorageValue<_, u8, ResultQuery<'static>>;
}
fn main() {
}
@@ -0,0 +1,5 @@
error: Invalid pallet::storage, unexpected generic argument kind, expected a type path to a `PalletError` enum variant, found `'static`
--> tests/pallet_ui/storage_result_query_wrong_generic_kind.rs:18:56
|
18 | type Foo<T: Config> = StorageValue<_, u8, ResultQuery<'static>>;
| ^^^^^^^