1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Datatype for easy mutation of the extra "sidecar" data.
use super::*;
/// A mutator type allowing inspection and possible modification of the extra "sidecar" data.
///
/// This may be used as a `Deref` for the pallet's extra data. If mutated (using `DerefMut`), then
/// any uncommitted changes (see `commit` function) will be automatically committed to storage when
/// dropped. Changes, even after committed, may be reverted to their original values with the
/// `revert` function.
pub struct ExtraMutator<T: Config<I>, I: 'static = ()> {
id: T::AssetId,
who: T::AccountId,
original: T::Extra,
pending: Option<T::Extra>,
}
impl<T: Config<I>, I: 'static> Drop for ExtraMutator<T, I> {
fn drop(&mut self) {
debug_assert!(self.commit().is_ok(), "attempt to write to non-existent asset account");
}
}
impl<T: Config<I>, I: 'static> core::ops::Deref for ExtraMutator<T, I> {
type Target = T::Extra;
fn deref(&self) -> &T::Extra {
match self.pending {
Some(ref value) => value,
None => &self.original,
}
}
}
impl<T: Config<I>, I: 'static> core::ops::DerefMut for ExtraMutator<T, I> {
fn deref_mut(&mut self) -> &mut T::Extra {
if self.pending.is_none() {
self.pending = Some(self.original.clone());
}
self.pending.as_mut().unwrap()
}
}
impl<T: Config<I>, I: 'static> ExtraMutator<T, I> {
pub(super) fn maybe_new(
id: T::AssetId,
who: impl core::borrow::Borrow<T::AccountId>,
) -> Option<ExtraMutator<T, I>> {
if let Some(a) = Account::<T, I>::get(&id, who.borrow()) {
Some(ExtraMutator::<T, I> {
id,
who: who.borrow().clone(),
original: a.extra,
pending: None,
})
} else {
None
}
}
/// Commit any changes to storage.
pub fn commit(&mut self) -> Result<(), ()> {
if let Some(extra) = self.pending.take() {
Account::<T, I>::try_mutate(&self.id, &self.who, |maybe_account| {
maybe_account.as_mut().ok_or(()).map(|account| account.extra = extra)
})
} else {
Ok(())
}
}
/// Revert any changes, even those already committed by `self` and drop self.
pub fn revert(mut self) -> Result<(), ()> {
self.pending = None;
Account::<T, I>::try_mutate(&self.id, &self.who, |maybe_account| {
maybe_account
.as_mut()
.ok_or(())
.map(|account| account.extra = self.original.clone())
})
}
}