mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 19:01:08 +00:00
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:
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
+15
@@ -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>>;
|
||||
| ~~~~~~~~
|
||||
+23
@@ -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() {
|
||||
}
|
||||
+5
@@ -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>>;
|
||||
| ^^^^^
|
||||
+16
@@ -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() {
|
||||
}
|
||||
+5
@@ -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>>;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
+22
@@ -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() {
|
||||
}
|
||||
+5
@@ -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)>;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
+22
@@ -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() {
|
||||
}
|
||||
+5
@@ -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>>;
|
||||
| ^^^^^^^
|
||||
Reference in New Issue
Block a user