diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 40bc878cff365..e7b2db4aa9b51 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -874,6 +874,7 @@ pub mod tests { decl_storage! { trait Store for Module as Test { + pub Value get(fn value): u64; 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; @@ -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; + + 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; + + 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(|| { @@ -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::()), + default: vec![0, 0, 0, 0, 0, 0, 0, 0], + docs: vec![], + }, StorageEntryMetadata { name: "Data", modifier: StorageEntryModifier::Default, diff --git a/frame/support/src/storage/generator/value.rs b/frame/support/src/storage/generator/value.rs index 55b3487b1324c..4a1fd5c551d3f 100644 --- a/frame/support/src/storage/generator/value.rs +++ b/frame/support/src/storage/generator/value.rs @@ -118,6 +118,30 @@ impl> storage::StorageValue for G { ret } + fn mutate_exists(f: F) -> R + where + F: FnOnce(&mut Option) -> R, + { + Self::try_mutate_exists(|v| Ok::(f(v))) + .expect("`Never` can not be constructed; qed") + } + + fn try_mutate_exists(f: F) -> Result + where + F: FnOnce(&mut Option) -> Result, + { + 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); diff --git a/frame/support/src/storage/mod.rs b/frame/support/src/storage/mod.rs index 8c0d6207c3f4d..28f2dee992281 100644 --- a/frame/support/src/storage/mod.rs +++ b/frame/support/src/storage/mod.rs @@ -114,6 +114,12 @@ pub trait StorageValue { /// Mutate the value if closure returns `Ok` fn try_mutate Result>(f: F) -> Result; + /// Mutate the value. Deletes the item if mutated to a `None`. + fn mutate_exists) -> R>(f: F) -> R; + + /// Mutate the value if closure returns `Ok`. Deletes the item if mutated to a `None`. + fn try_mutate_exists) -> Result>(f: F) -> Result; + /// Clear the storage value. fn kill(); diff --git a/frame/support/src/storage/types/value.rs b/frame/support/src/storage/types/value.rs index f145e9fb30414..15290f1b1e085 100644 --- a/frame/support/src/storage/types/value.rs +++ b/frame/support/src/storage/types/value.rs @@ -142,6 +142,18 @@ where >::try_mutate(f) } + /// Mutate the value. Deletes the item if mutated to a `None`. + pub fn mutate_exists) -> R>(f: F) -> R { + >::mutate_exists(f) + } + + /// Mutate the value if closure returns `Ok`. Deletes the item if mutated to a `None`. + pub fn try_mutate_exists) -> Result>( + f: F, + ) -> Result { + >::try_mutate_exists(f) + } + /// Clear the storage value. pub fn kill() { >::kill()