Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

mutate_exists for StorageValue with ValueQuery #13245

Merged
merged 4 commits into from
Jan 27, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions frame/support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,7 @@ pub mod tests {

decl_storage! {
trait Store for Module<T: Config> as Test {
pub Value get(fn value): u64;
ggwpez marked this conversation as resolved.
Show resolved Hide resolved
pub Data get(fn data) build(|_| vec![(15u32, 42u64)]):
map hasher(twox_64_concat) u32 => u64;
pub OptionLinkedMap: map hasher(blake2_128_concat) u32 => Option<u32>;
Expand Down Expand Up @@ -946,6 +947,61 @@ pub mod tests {
});
}

#[test]
fn storage_value_mutate_exists_should_work() {
new_test_ext().execute_with(|| {
#[crate::storage_alias]
pub type Value = StorageValue<Test, u32>;

assert!(!Value::exists());

Value::mutate_exists(|v| *v = Some(1));
assert!(Value::exists());
assert_eq!(Value::get(), Some(1));

// removed if mutated to `None`
Value::mutate_exists(|v| *v = None);
assert!(!Value::exists());
});
}

#[test]
fn storage_value_try_mutate_exists_should_work() {
new_test_ext().execute_with(|| {
#[crate::storage_alias]
pub type Value = StorageValue<Test, u32>;

type TestResult = result::Result<(), &'static str>;

assert!(!Value::exists());

// mutated if `Ok`
assert_ok!(Value::try_mutate_exists(|v| -> TestResult {
*v = Some(1);
Ok(())
}));
assert!(Value::exists());
assert_eq!(Value::get(), Some(1));

// no-op if `Err`
assert_noop!(
Value::try_mutate_exists(|v| -> TestResult {
*v = Some(2);
Err("nah")
}),
"nah"
);
assert_eq!(Value::get(), Some(1));

// removed if mutated to`None`
assert_ok!(Value::try_mutate_exists(|v| -> TestResult {
*v = None;
Ok(())
}));
assert!(!Value::exists());
});
}

#[test]
fn map_issue_3318() {
new_test_ext().execute_with(|| {
Expand Down Expand Up @@ -1257,6 +1313,13 @@ pub mod tests {
PalletStorageMetadata {
prefix: "Test",
entries: vec![
StorageEntryMetadata {
name: "Value",
modifier: StorageEntryModifier::Default,
ty: StorageEntryType::Plain(scale_info::meta_type::<u32>()),
Copy link
Member

@ggwpez ggwpez Jan 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ty: StorageEntryType::Plain(scale_info::meta_type::<u32>()),
ty: StorageEntryType::Plain(scale_info::meta_type::<u64>()),

You can also run the tests locally with cargo t -p frame-support.

default: vec![0, 0, 0, 0, 0, 0, 0, 0],
docs: vec![],
},
StorageEntryMetadata {
name: "Data",
modifier: StorageEntryModifier::Default,
Expand Down
24 changes: 24 additions & 0 deletions frame/support/src/storage/generator/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,30 @@ impl<T: FullCodec, G: StorageValue<T>> storage::StorageValue<T> for G {
ret
}

fn mutate_exists<R, F>(f: F) -> R
where
F: FnOnce(&mut Option<T>) -> R,
{
Self::try_mutate_exists(|v| Ok::<R, Never>(f(v)))
.expect("`Never` can not be constructed; qed")
}

fn try_mutate_exists<R, E, F>(f: F) -> Result<R, E>
where
F: FnOnce(&mut Option<T>) -> Result<R, E>,
{
let mut val = G::from_query_to_optional_value(Self::get());

let ret = f(&mut val);
if ret.is_ok() {
match val {
Some(ref val) => Self::put(val),
None => Self::kill(),
}
}
ret
}

fn take() -> G::Query {
let key = Self::storage_value_final_key();
let value = unhashed::get(&key);
Expand Down
6 changes: 6 additions & 0 deletions frame/support/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ pub trait StorageValue<T: FullCodec> {
/// Mutate the value if closure returns `Ok`
fn try_mutate<R, E, F: FnOnce(&mut Self::Query) -> Result<R, E>>(f: F) -> Result<R, E>;

/// Mutate the value. Deletes the item if mutated to a `None`.
fn mutate_exists<R, F: FnOnce(&mut Option<T>) -> R>(f: F) -> R;

/// Mutate the value if closure returns `Ok`. Deletes the item if mutated to a `None`.
fn try_mutate_exists<R, E, F: FnOnce(&mut Option<T>) -> Result<R, E>>(f: F) -> Result<R, E>;

/// Clear the storage value.
fn kill();

Expand Down
12 changes: 12 additions & 0 deletions frame/support/src/storage/types/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ where
<Self as crate::storage::StorageValue<Value>>::try_mutate(f)
}

/// Mutate the value. Deletes the item if mutated to a `None`.
pub fn mutate_exists<R, F: FnOnce(&mut Option<Value>) -> R>(f: F) -> R {
<Self as crate::storage::StorageValue<Value>>::mutate_exists(f)
}

/// Mutate the value if closure returns `Ok`. Deletes the item if mutated to a `None`.
pub fn try_mutate_exists<R, E, F: FnOnce(&mut Option<Value>) -> Result<R, E>>(
f: F,
) -> Result<R, E> {
<Self as crate::storage::StorageValue<Value>>::try_mutate_exists(f)
}

/// Clear the storage value.
pub fn kill() {
<Self as crate::storage::StorageValue<Value>>::kill()
Expand Down