Maps are appendable too (#2716)

* Maps are appendable too

* Update srml/support/src/storage/hashed/generator.rs

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update srml/support/src/storage/mod.rs

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Fix docs

* Make Appendable public

* Bump runtime version
This commit is contained in:
Gavin Wood
2019-05-29 15:15:10 +02:00
committed by GitHub
parent 810d2712da
commit fccc55160a
7 changed files with 94 additions and 5 deletions
+1 -1
View File
@@ -59,7 +59,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
impl_name: create_runtime_str!("substrate-node"),
authoring_version: 10,
spec_version: 88,
impl_version: 88,
impl_version: 89,
apis: RUNTIME_API_VERSIONS,
};
@@ -225,8 +225,11 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
#mutate_impl ;
ret
}
}
impl<#traitinstance: 'static + #traittype, #instance #bound_instantiable>
#scrate::storage::hashed::generator::AppendableStorageMap<#kty, #typ> for #name<#traitinstance, #instance>
{}
}
}
+3 -1
View File
@@ -57,7 +57,9 @@ pub mod unsigned;
mod double_map;
pub mod traits;
pub use self::storage::{StorageList, StorageValue, StorageMap, EnumerableStorageMap, StorageDoubleMap};
pub use self::storage::{
StorageList, StorageValue, StorageMap, EnumerableStorageMap, StorageDoubleMap, AppendableStorageMap
};
pub use self::hashable::Hashable;
pub use self::dispatch::{Parameter, Dispatchable, Callable, IsSubType};
pub use self::double_map::StorageDoubleMapWithHasher;
@@ -281,5 +281,25 @@ pub trait EnumerableStorageMap<K: codec::Codec, V: codec::Codec>: StorageMap<K,
fn head<S: HashedStorage<Self::Hasher>>(storage: &S) -> Option<K>;
/// Enumerate all elements in the map.
fn enumerate<'a, S: HashedStorage<Self::Hasher>>(storage: &'a S) -> Box<dyn Iterator<Item = (K, V)> + 'a> where K: 'a, V: 'a;
fn enumerate<'a, S: HashedStorage<Self::Hasher>>(
storage: &'a S
) -> Box<dyn Iterator<Item = (K, V)> + 'a> where K: 'a, V: 'a;
}
/// A `StorageMap` with appendable entries.
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> {
let k = Self::key_for(key);
let new_val = <V as codec::EncodeAppend>::append(
storage.get_raw(&k[..]).unwrap_or_default(),
items,
).ok_or_else(|| "Could not append given item")?;
storage.put_raw(&k[..], &new_val);
Ok(())
}
}
+22 -1
View File
@@ -330,6 +330,25 @@ impl<K: Codec, V: Codec, U> StorageMap<K, V> for U where U: hashed::generator::S
}
}
/// A storage map with values that can be appended to.
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>;
}
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>
{
U::append(key.borrow(), items, &mut RuntimeStorage)
}
}
/// A storage map that can be enumerated.
///
/// Primarily useful for off-chain computations.
@@ -342,7 +361,9 @@ pub trait EnumerableStorageMap<K: Codec, V: Codec>: StorageMap<K, V> {
fn enumerate() -> Box<dyn Iterator<Item = (K, V)>> where K: 'static, V: 'static;
}
impl<K: Codec, V: Codec, U> EnumerableStorageMap<K, V> for U where U: hashed::generator::EnumerableStorageMap<K, V> {
impl<K: Codec, V: Codec, U> EnumerableStorageMap<K, V> for U
where U: hashed::generator::EnumerableStorageMap<K, V>
{
fn head() -> Option<K> {
<U as hashed::generator::EnumerableStorageMap<K, V>>::head(&RuntimeStorage)
}
@@ -900,3 +900,46 @@ mod test3 {
type BlockNumber = u32;
}
}
#[cfg(test)]
#[allow(dead_code)]
mod test_map_vec_append {
pub trait Trait {
type Origin;
type BlockNumber;
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
}
crate::decl_storage! {
trait Store for Module<T: Trait> as Test {
JustVec: Vec<u32>;
MapVec: map u32 => Vec<u32>;
}
}
struct Test {}
impl Trait for Test {
type Origin = u32;
type BlockNumber = u32;
}
#[test]
fn append_works() {
use crate::storage::{AppendableStorageMap, StorageMap, StorageValue};
use runtime_io::{with_externalities, TestExternalities};
with_externalities(&mut TestExternalities::default(), || {
let _ = <MapVec<Test>>::append(1, &[1, 2, 3]);
let _ = <MapVec<Test>>::append(1, &[4, 5]);
assert_eq!(<MapVec<Test>>::get(1), vec![1, 2, 3, 4, 5]);
let _ = <JustVec<Test>>::append(&[1, 2, 3]);
let _ = <JustVec<Test>>::append(&[4, 5]);
assert_eq!(<JustVec<Test>>::get(), vec![1, 2, 3, 4, 5]);
});
}
}