diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index 902893972f0b12bb130e0ed4ce162c6b3033662a..77ca89dc6f80024aa569fe89072c2471fa51d304 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -874,6 +874,7 @@ pub mod tests { decl_storage! { trait Store for Module<T: Config> 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<u32>; @@ -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(|| { @@ -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::<u64>()), + default: vec![0, 0, 0, 0, 0, 0, 0, 0], + docs: vec![], + }, StorageEntryMetadata { name: "Data", modifier: StorageEntryModifier::Default, diff --git a/substrate/frame/support/src/storage/generator/value.rs b/substrate/frame/support/src/storage/generator/value.rs index 55b3487b1324c1351bfd5cfb7d0a683104b78e74..4a1fd5c551d3fe14368dd3b8e0a1ee75f84ef460 100644 --- a/substrate/frame/support/src/storage/generator/value.rs +++ b/substrate/frame/support/src/storage/generator/value.rs @@ -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); diff --git a/substrate/frame/support/src/storage/mod.rs b/substrate/frame/support/src/storage/mod.rs index 8c0d6207c3f4d0f3e686c8c9c855b3ae85e70b13..28f2dee992281cca114ca6e0a8f003a6fe8af54b 100644 --- a/substrate/frame/support/src/storage/mod.rs +++ b/substrate/frame/support/src/storage/mod.rs @@ -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(); diff --git a/substrate/frame/support/src/storage/types/value.rs b/substrate/frame/support/src/storage/types/value.rs index f145e9fb304146db885896dd88217fcdaec53509..15290f1b1e08540c1b524e4c5ace20b279bdb1b6 100644 --- a/substrate/frame/support/src/storage/types/value.rs +++ b/substrate/frame/support/src/storage/types/value.rs @@ -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()