mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 07:27:55 +00:00
Use optimized append and len storage methods in SRML. (#3071)
* expose len from codec to storage. * refactor runtime with len and append. * Undo example. * Remove imports. * Bump codec. * Optionify. * Make decode_len counscious. * Refactor. * Update srml/support/src/storage/hashed/generator.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update srml/support/src/storage/hashed/generator.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update srml/support/src/storage/hashed/generator.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update srml/support/src/storage/hashed/generator.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Fix merge. * fix some docs. * Add NoDefault trait. * Bump. * Final nits. * Update srml/support/src/traits.rs * new approach toward len. * re-create lock file. * Fix build errors and Option handling. * More test fix * Use default for append as well. * Fix runtime. * Add support for linked_map * More tweaks from review. * Fix style * Change api for none-values * Bump.
This commit is contained in:
committed by
Bastian Köcher
parent
99a7492dbf
commit
095c7de7ff
@@ -62,7 +62,8 @@ mod double_map;
|
||||
pub mod traits;
|
||||
|
||||
pub use self::storage::{
|
||||
StorageValue, StorageMap, EnumerableStorageMap, StorageDoubleMap, AppendableStorageMap
|
||||
StorageValue, StorageMap, EnumerableStorageMap, StorageDoubleMap, AppendableStorageMap,
|
||||
DecodeLengthStorageMap,
|
||||
};
|
||||
pub use self::hashable::Hashable;
|
||||
pub use self::dispatch::{Parameter, Dispatchable, Callable, IsSubType};
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Abstract storage to use on HashedStorage trait
|
||||
//! Abstract storage to use on HashedStorage trait. Please refer to the
|
||||
//! [top level docs](../../index.html) for more detailed documentation about storage traits and functions.
|
||||
|
||||
use crate::codec::{self, Encode};
|
||||
use crate::rstd::prelude::{Vec, Box};
|
||||
use crate::rstd::{prelude::{Vec, Box}, iter::FromIterator};
|
||||
#[cfg(feature = "std")]
|
||||
use crate::storage::unhashed::generator::UnhashedStorage;
|
||||
use crate::traits::{StorageDefault, Len};
|
||||
use runtime_io::{twox_64, twox_128, blake2_128, twox_256, blake2_256};
|
||||
|
||||
pub trait StorageHasher: 'static {
|
||||
@@ -164,6 +166,9 @@ impl<H: StorageHasher> HashedStorage<H> for sr_primitives::StorageOverlay {
|
||||
pub trait StorageValue<T: codec::Codec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
/// Something that can provide the default value of this storage type.
|
||||
type Default: StorageDefault<T>;
|
||||
|
||||
|
||||
/// Get the storage key.
|
||||
fn key() -> &'static [u8];
|
||||
@@ -202,24 +207,75 @@ pub trait StorageValue<T: codec::Codec> {
|
||||
/// Append the given items to the value in the storage.
|
||||
///
|
||||
/// `T` is required to implement `codec::EncodeAppend`.
|
||||
fn append<S: HashedStorage<Twox128>, I: codec::Encode>(
|
||||
items: &[I], storage: &mut S
|
||||
) -> Result<(), &'static str> where T: codec::EncodeAppend<Item=I> {
|
||||
fn append<'a, S, I, R>(
|
||||
items: R,
|
||||
storage: &mut S,
|
||||
) -> Result<(), &'static str> where
|
||||
S: HashedStorage<Twox128>,
|
||||
I: 'a + codec::Encode,
|
||||
T: codec::EncodeAppend<Item=I>,
|
||||
R: IntoIterator<Item=&'a I>,
|
||||
R::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let new_val = <T as codec::EncodeAppend>::append(
|
||||
storage.get_raw(Self::key()).unwrap_or_default(),
|
||||
// if the key exists, directly append to it.
|
||||
storage.get_raw(Self::key()).unwrap_or_else(|| {
|
||||
// otherwise, try and read a proper __provided__ default.
|
||||
Self::Default::default().map(|v| v.encode())
|
||||
// or just use the Rust's `default()` value.
|
||||
.unwrap_or_default()
|
||||
}),
|
||||
items,
|
||||
).map_err(|_| "Could not append given item")?;
|
||||
storage.put_raw(Self::key(), &new_val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Safely append the given items to the value in the storage. If a codec error occurs, then the
|
||||
/// old (presumably corrupt) value is replaced with the given `items`.
|
||||
///
|
||||
/// `T` is required to implement `codec::EncodeAppend`.
|
||||
fn append_or_put<'a, S, I, R>(
|
||||
items: R,
|
||||
storage: &mut S,
|
||||
) where
|
||||
S: HashedStorage<Twox128>,
|
||||
I: 'a + codec::Encode + Clone,
|
||||
T: codec::EncodeAppend<Item=I> + FromIterator<I>,
|
||||
R: IntoIterator<Item=&'a I> + Clone,
|
||||
R::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
Self::append(items.clone(), storage)
|
||||
.unwrap_or_else(|_| Self::put(&items.into_iter().cloned().collect(), storage));
|
||||
}
|
||||
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
///
|
||||
/// Note that `0` is returned as the default value if no encoded value exists at the given key.
|
||||
/// Therefore, this function cannot be used as a sign of _existence_. use the `::exists()`
|
||||
/// function for this purpose.
|
||||
fn decode_len<S: HashedStorage<Twox128>>(storage: &mut S) -> Result<usize, &'static str>
|
||||
where T: codec::DecodeLength, T: Len
|
||||
{
|
||||
// attempt to get the length directly.
|
||||
if let Some(k) = storage.get_raw(Self::key()) {
|
||||
<T as codec::DecodeLength>::len(&k).map_err(|e| e.what())
|
||||
} else {
|
||||
Ok(Self::Default::default().map(|v| v.len()).unwrap_or(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A strongly-typed map in storage.
|
||||
pub trait StorageMap<K: codec::Codec, V: codec::Codec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
/// Hasher type
|
||||
type Hasher: StorageHasher;
|
||||
/// Something that can provide the default value of this storage type.
|
||||
type Default: StorageDefault<V>;
|
||||
|
||||
/// Get the prefix key in storage.
|
||||
fn prefix() -> &'static [u8];
|
||||
@@ -295,16 +351,69 @@ pub trait EnumerableStorageMap<K: codec::Codec, V: codec::Codec>: StorageMap<K,
|
||||
pub trait AppendableStorageMap<K: codec::Codec, V: codec::Codec>: StorageMap<K, V> {
|
||||
/// Append the given items to the value in the storage.
|
||||
///
|
||||
/// `T` is required to implement `codec::EncodeAppend`.
|
||||
fn append<S: HashedStorage<Self::Hasher>, I: codec::Encode>(
|
||||
key : &K, items: &[I], storage: &mut S
|
||||
) -> Result<(), &'static str> where V: codec::EncodeAppend<Item=I> {
|
||||
/// `V` is required to implement `codec::EncodeAppend`.
|
||||
fn append<'a, S, I, R>(
|
||||
key : &K,
|
||||
items: R,
|
||||
storage: &mut S,
|
||||
) -> Result<(), &'static str> where
|
||||
S: HashedStorage<Self::Hasher>,
|
||||
I: 'a + codec::Encode,
|
||||
V: codec::EncodeAppend<Item=I>,
|
||||
R: IntoIterator<Item=&'a I> + Clone,
|
||||
R::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let k = Self::key_for(key);
|
||||
let new_val = <V as codec::EncodeAppend>::append(
|
||||
storage.get_raw(&k[..]).unwrap_or_default(),
|
||||
storage.get_raw(&k[..]).unwrap_or_else(|| {
|
||||
// otherwise, try and read a proper __provided__ default.
|
||||
Self::Default::default().map(|v| v.encode())
|
||||
// or just use the default value.
|
||||
.unwrap_or_default()
|
||||
}),
|
||||
items,
|
||||
).map_err(|_| "Could not append given item")?;
|
||||
storage.put_raw(&k[..], &new_val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Safely append the given items to the value in the storage. If a codec error occurs, then the
|
||||
/// old (presumably corrupt) value is replaced with the given `items`.
|
||||
///
|
||||
/// `T` is required to implement `codec::EncodeAppend`.
|
||||
fn append_or_insert<'a, S, I, R>(
|
||||
key : &K,
|
||||
items: R,
|
||||
storage: &mut S,
|
||||
) where
|
||||
S: HashedStorage<Self::Hasher>,
|
||||
I: 'a + codec::Encode + Clone,
|
||||
V: codec::EncodeAppend<Item=I> + crate::rstd::iter::FromIterator<I>,
|
||||
R: IntoIterator<Item=&'a I> + Clone,
|
||||
R::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
Self::append(key, items.clone(), storage)
|
||||
.unwrap_or_else(|_| Self::insert(key, &items.into_iter().cloned().collect(), storage));
|
||||
}
|
||||
}
|
||||
|
||||
/// A storage map with a decodable length.
|
||||
pub trait DecodeLengthStorageMap<K: codec::Codec, V: codec::Codec>: StorageMap<K, V> {
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
///
|
||||
/// Note that `0` is returned as the default value if no encoded value exists at the given key.
|
||||
/// Therefore, this function cannot be used as a sign of _existence_. use the `::exists()`
|
||||
/// function for this purpose.
|
||||
fn decode_len<S: HashedStorage<Self::Hasher>>(key: &K, storage: &mut S) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength, V: Len
|
||||
{
|
||||
let k = Self::key_for(key);
|
||||
if let Some(v) = storage.get_raw(&k[..]) {
|
||||
<V as codec::DecodeLength>::len(&v).map_err(|e| e.what())
|
||||
} else {
|
||||
Ok(Self::Default::default().map(|v| v.len()).unwrap_or(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
//! Stuff to do with the runtime's storage.
|
||||
|
||||
use crate::rstd::prelude::*;
|
||||
use crate::rstd::borrow::Borrow;
|
||||
use crate::rstd::{borrow::Borrow, iter::FromIterator};
|
||||
use codec::{Codec, Encode, Decode, KeyedVec, EncodeAppend};
|
||||
use hashed::generator::{HashedStorage, StorageHasher};
|
||||
use unhashed::generator::UnhashedStorage;
|
||||
use crate::traits::{StorageDefault, Len};
|
||||
|
||||
#[macro_use]
|
||||
pub mod storage_items;
|
||||
@@ -107,6 +108,8 @@ impl UnhashedStorage for RuntimeStorage {
|
||||
pub trait StorageValue<T: Codec> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
/// Something that can provide the default value of this storage type.
|
||||
type Default: StorageDefault<T>;
|
||||
|
||||
/// Get the storage key.
|
||||
fn key() -> &'static [u8];
|
||||
@@ -136,12 +139,39 @@ pub trait StorageValue<T: Codec> {
|
||||
/// Append the given item to the value in the storage.
|
||||
///
|
||||
/// `T` is required to implement `codec::EncodeAppend`.
|
||||
fn append<I: Encode>(items: &[I]) -> Result<(), &'static str>
|
||||
where T: EncodeAppend<Item=I>;
|
||||
fn append<'a, I, R>(items: R) -> Result<(), &'static str> where
|
||||
I: 'a + Encode,
|
||||
T: EncodeAppend<Item=I>,
|
||||
R: IntoIterator<Item=&'a I>,
|
||||
R::IntoIter: ExactSizeIterator;
|
||||
|
||||
/// Append the given items to the value in the storage.
|
||||
///
|
||||
/// `T` is required to implement `Codec::EncodeAppend`.
|
||||
///
|
||||
/// Upon any failure, it replaces `items` as the new value (assuming that the previous stored
|
||||
/// data is simply corrupt and no longer usable).
|
||||
///
|
||||
/// ### WARNING
|
||||
///
|
||||
/// use with care; if your use-case is not _exactly_ as what this function is doing,
|
||||
/// you should use append and sensibly handle failure within the runtime code if it happens.
|
||||
fn append_or_put<'a, I, R>(items: R) where
|
||||
I: 'a + Encode + Clone,
|
||||
T: EncodeAppend<Item=I> + FromIterator<I>,
|
||||
R: IntoIterator<Item=&'a I> + Clone,
|
||||
R::IntoIter: ExactSizeIterator;
|
||||
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
fn decode_len() -> Result<usize, &'static str>
|
||||
where T: codec::DecodeLength, T: Len;
|
||||
}
|
||||
|
||||
impl<T: Codec, U> StorageValue<T> for U where U: hashed::generator::StorageValue<T> {
|
||||
type Query = U::Query;
|
||||
type Default = U::Default;
|
||||
|
||||
fn key() -> &'static [u8] {
|
||||
<U as hashed::generator::StorageValue<T>>::key()
|
||||
@@ -167,17 +197,35 @@ impl<T: Codec, U> StorageValue<T> for U where U: hashed::generator::StorageValue
|
||||
fn take() -> Self::Query {
|
||||
U::take(&mut RuntimeStorage)
|
||||
}
|
||||
fn append<I: Encode>(items: &[I]) -> Result<(), &'static str>
|
||||
where T: EncodeAppend<Item=I>
|
||||
fn append<'a, I, R>(items: R) -> Result<(), &'static str> where
|
||||
I: 'a + Encode,
|
||||
T: EncodeAppend<Item=I>,
|
||||
R: IntoIterator<Item=&'a I>,
|
||||
R::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
U::append(items, &mut RuntimeStorage)
|
||||
}
|
||||
fn append_or_put<'a, I, R>(items: R) where
|
||||
I: 'a + Encode + Clone,
|
||||
T: EncodeAppend<Item=I> + FromIterator<I>,
|
||||
R: IntoIterator<Item=&'a I> + Clone,
|
||||
R::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
U::append_or_put(items, &mut RuntimeStorage)
|
||||
}
|
||||
fn decode_len() -> Result<usize, &'static str>
|
||||
where T: codec::DecodeLength, T: Len
|
||||
{
|
||||
U::decode_len(&mut RuntimeStorage)
|
||||
}
|
||||
}
|
||||
|
||||
/// A strongly-typed map in storage.
|
||||
pub trait StorageMap<K: Codec, V: Codec> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
/// Something that can provide the default value of this storage type.
|
||||
type Default: StorageDefault<V>;
|
||||
|
||||
/// Get the prefix key in storage.
|
||||
fn prefix() -> &'static [u8];
|
||||
@@ -213,6 +261,7 @@ pub trait StorageMap<K: Codec, V: Codec> {
|
||||
|
||||
impl<K: Codec, V: Codec, U> StorageMap<K, V> for U where U: hashed::generator::StorageMap<K, V> {
|
||||
type Query = U::Query;
|
||||
type Default = U::Default;
|
||||
|
||||
fn prefix() -> &'static [u8] {
|
||||
<U as hashed::generator::StorageMap<K, V>>::prefix()
|
||||
@@ -260,18 +309,85 @@ pub trait AppendableStorageMap<K: Codec, V: Codec>: StorageMap<K, V> {
|
||||
/// Append the given item to the value in the storage.
|
||||
///
|
||||
/// `T` is required to implement `codec::EncodeAppend`.
|
||||
fn append<KeyArg: Borrow<K>, I: Encode>(key: KeyArg, items: &[I]) -> Result<(), &'static str>
|
||||
where V: EncodeAppend<Item=I>;
|
||||
fn append<'a, KeyArg, I, R>(
|
||||
key: KeyArg,
|
||||
items: R,
|
||||
) -> Result<(), &'static str> where
|
||||
KeyArg: Borrow<K>,
|
||||
I: 'a + codec::Encode,
|
||||
V: EncodeAppend<Item=I>,
|
||||
R: IntoIterator<Item=&'a I> + Clone,
|
||||
R::IntoIter: ExactSizeIterator;
|
||||
|
||||
/// Append the given items to the value in the storage.
|
||||
///
|
||||
/// `T` is required to implement `codec::EncodeAppend`.
|
||||
///
|
||||
/// Upon any failure, it replaces `items` as the new value (assuming that the previous stored
|
||||
/// data is simply corrupt and no longer usable).
|
||||
///
|
||||
/// WARNING: use with care; if your use-case is not _exactly_ as what this function is doing,
|
||||
/// you should use append and sensibly handle failure within the runtime code if it happens.
|
||||
fn append_or_insert<'a, KeyArg, I, R>(
|
||||
key: KeyArg,
|
||||
items: R,
|
||||
) where
|
||||
KeyArg: Borrow<K>,
|
||||
I: 'a + codec::Encode + Clone,
|
||||
V: codec::EncodeAppend<Item=I> + FromIterator<I>,
|
||||
R: IntoIterator<Item=&'a I> + Clone,
|
||||
R::IntoIter: ExactSizeIterator;
|
||||
}
|
||||
|
||||
impl<K: Codec, V: Codec, U> AppendableStorageMap<K, V> for U
|
||||
where U: hashed::generator::AppendableStorageMap<K, V>
|
||||
{
|
||||
fn append<KeyArg: Borrow<K>, I: Encode>(key: KeyArg, items: &[I]) -> Result<(), &'static str>
|
||||
where V: EncodeAppend<Item=I>
|
||||
fn append<'a, KeyArg, I, R>(
|
||||
key: KeyArg,
|
||||
items: R,
|
||||
) -> Result<(), &'static str> where
|
||||
KeyArg: Borrow<K>,
|
||||
I: 'a + codec::Encode,
|
||||
V: EncodeAppend<Item=I>,
|
||||
R: IntoIterator<Item=&'a I> + Clone,
|
||||
R::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
U::append(key.borrow(), items, &mut RuntimeStorage)
|
||||
}
|
||||
|
||||
fn append_or_insert<'a, KeyArg, I, R>(
|
||||
key: KeyArg,
|
||||
items: R,
|
||||
) where
|
||||
KeyArg: Borrow<K>,
|
||||
I: 'a + codec::Encode + Clone,
|
||||
V: codec::EncodeAppend<Item=I> + FromIterator<I>,
|
||||
R: IntoIterator<Item=&'a I> + Clone,
|
||||
R::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
U::append_or_insert(key.borrow(), items, &mut RuntimeStorage)
|
||||
}
|
||||
}
|
||||
|
||||
/// A storage map with a decodable length.
|
||||
pub trait DecodeLengthStorageMap<K: Codec, V: Codec>: StorageMap<K, V> {
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
///
|
||||
/// Has the same logic as [`StorageValue`](trait.StorageValue.html).
|
||||
fn decode_len<KeyArg: Borrow<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength, V: Len;
|
||||
}
|
||||
|
||||
impl <K: Codec, V: Codec, U> DecodeLengthStorageMap<K, V> for U
|
||||
where U: hashed::generator::DecodeLengthStorageMap<K, V>
|
||||
{
|
||||
fn decode_len<KeyArg: Borrow<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength, V: Len
|
||||
{
|
||||
U::decode_len(key.borrow(), &mut RuntimeStorage)
|
||||
}
|
||||
}
|
||||
|
||||
/// A storage map that can be enumerated.
|
||||
|
||||
@@ -172,6 +172,7 @@ macro_rules! __storage_items_internal {
|
||||
|
||||
impl $crate::storage::hashed::generator::StorageValue<$ty> for $name {
|
||||
type Query = $gettype;
|
||||
type Default = ();
|
||||
|
||||
/// Get the storage key.
|
||||
fn key() -> &'static [u8] {
|
||||
@@ -221,8 +222,8 @@ macro_rules! __storage_items_internal {
|
||||
|
||||
impl $crate::storage::hashed::generator::StorageMap<$kty, $ty> for $name {
|
||||
type Query = $gettype;
|
||||
|
||||
type Hasher = $crate::Blake2_256;
|
||||
type Default = ();
|
||||
|
||||
/// Get the prefix key in storage.
|
||||
fn prefix() -> &'static [u8] {
|
||||
@@ -795,18 +796,41 @@ mod test3 {
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
mod test_map_vec_append {
|
||||
mod test_append_and_len {
|
||||
use crate::storage::{AppendableStorageMap, DecodeLengthStorageMap, StorageMap, StorageValue};
|
||||
use runtime_io::{with_externalities, TestExternalities};
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
struct NoDef(u32);
|
||||
|
||||
crate::decl_storage! {
|
||||
trait Store for Module<T: Trait> as Test {
|
||||
NoDefault: Option<NoDef>;
|
||||
|
||||
JustVec: Vec<u32>;
|
||||
JustVecWithDefault: Vec<u32> = vec![6, 9];
|
||||
OptionVec: Option<Vec<u32>>;
|
||||
OptionVecWithDefault: Option<Vec<u32>> = Some(vec![6, 9]);
|
||||
|
||||
MapVec: map u32 => Vec<u32>;
|
||||
MapVecWithDefault: map u32 => Vec<u32> = vec![6, 9];
|
||||
OptionMapVec: map u32 => Option<Vec<u32>>;
|
||||
OptionMapVecWithDefault: map u32 => Option<Vec<u32>> = Some(vec![6, 9]);
|
||||
|
||||
LinkedMapVec: linked_map u32 => Vec<u32>;
|
||||
LinkedMapVecWithDefault: linked_map u32 => Vec<u32> = vec![6, 9];
|
||||
OptionLinkedMapVec: linked_map u32 => Option<Vec<u32>>;
|
||||
OptionLinkedMapVecWithDefault: linked_map u32 => Option<Vec<u32>> = Some(vec![6, 9]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -818,20 +842,115 @@ mod test_map_vec_append {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append_works() {
|
||||
use crate::storage::{AppendableStorageMap, StorageMap, StorageValue};
|
||||
use runtime_io::{with_externalities, TestExternalities};
|
||||
|
||||
fn default_for_option() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
let _ = MapVec::append(1, &[1, 2, 3]);
|
||||
let _ = MapVec::append(1, &[4, 5]);
|
||||
assert_eq!(OptionVecWithDefault::get(), Some(vec![6, 9]));
|
||||
assert_eq!(OptionVec::get(), None);
|
||||
assert_eq!(JustVec::get(), vec![]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append_works() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
let _ = MapVec::append(1, [1, 2, 3].iter());
|
||||
let _ = MapVec::append(1, [4, 5].iter());
|
||||
assert_eq!(MapVec::get(1), vec![1, 2, 3, 4, 5]);
|
||||
|
||||
let _ = JustVec::append(&[1, 2, 3]);
|
||||
let _ = JustVec::append(&[4, 5]);
|
||||
let _ = JustVec::append([1, 2, 3].iter());
|
||||
let _ = JustVec::append([4, 5].iter());
|
||||
assert_eq!(JustVec::get(), vec![1, 2, 3, 4, 5]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append_works_for_default() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
assert_eq!(JustVecWithDefault::get(), vec![6, 9]);
|
||||
let _ = JustVecWithDefault::append([1].iter());
|
||||
assert_eq!(JustVecWithDefault::get(), vec![6, 9, 1]);
|
||||
|
||||
assert_eq!(MapVecWithDefault::get(0), vec![6, 9]);
|
||||
let _ = MapVecWithDefault::append(0, [1].iter());
|
||||
assert_eq!(MapVecWithDefault::get(0), vec![6, 9, 1]);
|
||||
|
||||
assert_eq!(OptionVec::get(), None);
|
||||
let _ = OptionVec::append([1].iter());
|
||||
assert_eq!(OptionVec::get(), Some(vec![1]));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append_or_put_works() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
let _ = MapVec::append_or_insert(1, [1, 2, 3].iter());
|
||||
let _ = MapVec::append_or_insert(1, [4, 5].iter());
|
||||
assert_eq!(MapVec::get(1), vec![1, 2, 3, 4, 5]);
|
||||
|
||||
let _ = JustVec::append_or_put([1, 2, 3].iter());
|
||||
let _ = JustVec::append_or_put([4, 5].iter());
|
||||
assert_eq!(JustVec::get(), vec![1, 2, 3, 4, 5]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn len_works() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
JustVec::put(&vec![1, 2, 3, 4]);
|
||||
OptionVec::put(&vec![1, 2, 3, 4, 5]);
|
||||
MapVec::insert(1, &vec![1, 2, 3, 4, 5, 6]);
|
||||
LinkedMapVec::insert(2, &vec![1, 2, 3]);
|
||||
|
||||
assert_eq!(JustVec::decode_len().unwrap(), 4);
|
||||
assert_eq!(OptionVec::decode_len().unwrap(), 5);
|
||||
assert_eq!(MapVec::decode_len(1).unwrap(), 6);
|
||||
assert_eq!(LinkedMapVec::decode_len(2).unwrap(), 3);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn len_works_for_default() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
// vec
|
||||
assert_eq!(JustVec::get(), vec![]);
|
||||
assert_eq!(JustVec::decode_len(), Ok(0));
|
||||
|
||||
assert_eq!(JustVecWithDefault::get(), vec![6, 9]);
|
||||
assert_eq!(JustVecWithDefault::decode_len(), Ok(2));
|
||||
|
||||
assert_eq!(OptionVec::get(), None);
|
||||
assert_eq!(OptionVec::decode_len(), Ok(0));
|
||||
|
||||
assert_eq!(OptionVecWithDefault::get(), Some(vec![6, 9]));
|
||||
assert_eq!(OptionVecWithDefault::decode_len(), Ok(2));
|
||||
|
||||
// map
|
||||
assert_eq!(MapVec::get(0), vec![]);
|
||||
assert_eq!(MapVec::decode_len(0), Ok(0));
|
||||
|
||||
assert_eq!(MapVecWithDefault::get(0), vec![6, 9]);
|
||||
assert_eq!(MapVecWithDefault::decode_len(0), Ok(2));
|
||||
|
||||
assert_eq!(OptionMapVec::get(0), None);
|
||||
assert_eq!(OptionMapVec::decode_len(0), Ok(0));
|
||||
|
||||
assert_eq!(OptionMapVecWithDefault::get(0), Some(vec![6, 9]));
|
||||
assert_eq!(OptionMapVecWithDefault::decode_len(0), Ok(2));
|
||||
|
||||
// linked map
|
||||
assert_eq!(LinkedMapVec::get(0), vec![]);
|
||||
assert_eq!(LinkedMapVec::decode_len(0), Ok(0));
|
||||
|
||||
assert_eq!(LinkedMapVecWithDefault::get(0), vec![6, 9]);
|
||||
assert_eq!(LinkedMapVecWithDefault::decode_len(0), Ok(2));
|
||||
|
||||
assert_eq!(OptionLinkedMapVec::get(0), None);
|
||||
assert_eq!(OptionLinkedMapVec::decode_len(0), Ok(0));
|
||||
|
||||
assert_eq!(OptionLinkedMapVecWithDefault::get(0), Some(vec![6, 9]));
|
||||
assert_eq!(OptionLinkedMapVecWithDefault::decode_len(0), Ok(2));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,28 @@ use crate::sr_primitives::ConsensusEngineId;
|
||||
|
||||
use super::for_each_tuple;
|
||||
|
||||
/// A trait that can return the default value of a storage item. This must only ever be implemented
|
||||
/// for a special delegator struct for each storage item
|
||||
pub trait StorageDefault<V>: Sized {
|
||||
/// Return the default value of type `V`. `None`, if `V` does not have a proper default value.
|
||||
fn default() -> Option<V>;
|
||||
}
|
||||
|
||||
// FIXME #1466 This is needed for `storage_items!`. Should be removed once it is deprecated.
|
||||
impl<T: Default> StorageDefault<T> for () { fn default() -> Option<T> { Some(Default::default()) } }
|
||||
|
||||
/// Anything that can have a `::len()` method.
|
||||
pub trait Len {
|
||||
/// Return the length of data type.
|
||||
fn len(&self) -> usize;
|
||||
}
|
||||
|
||||
impl<T: IntoIterator + Clone,> Len for T where <T as IntoIterator>::IntoIter: ExactSizeIterator {
|
||||
fn len(&self) -> usize {
|
||||
self.clone().into_iter().len()
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for querying a single fixed value from a type.
|
||||
pub trait Get<T> {
|
||||
/// Return a constant value.
|
||||
|
||||
Reference in New Issue
Block a user