use super::*;
use crate::traits::{
misc::{SameOrOther, TryDrop},
tokens::{AssetId, Balance},
};
use sp_runtime::{traits::Zero, RuntimeDebug};
use sp_std::marker::PhantomData;
pub trait HandleImbalanceDrop<AssetId, Balance> {
fn handle(asset: AssetId, amount: Balance);
}
#[must_use]
#[derive(RuntimeDebug, Eq, PartialEq)]
pub struct Imbalance<
A: AssetId,
B: Balance,
OnDrop: HandleImbalanceDrop<A, B>,
OppositeOnDrop: HandleImbalanceDrop<A, B>,
> {
asset: A,
amount: B,
_phantom: PhantomData<(OnDrop, OppositeOnDrop)>,
}
impl<
A: AssetId,
B: Balance,
OnDrop: HandleImbalanceDrop<A, B>,
OppositeOnDrop: HandleImbalanceDrop<A, B>,
> Drop for Imbalance<A, B, OnDrop, OppositeOnDrop>
{
fn drop(&mut self) {
if !self.amount.is_zero() {
OnDrop::handle(self.asset.clone(), self.amount)
}
}
}
impl<
A: AssetId,
B: Balance,
OnDrop: HandleImbalanceDrop<A, B>,
OppositeOnDrop: HandleImbalanceDrop<A, B>,
> TryDrop for Imbalance<A, B, OnDrop, OppositeOnDrop>
{
fn try_drop(self) -> Result<(), Self> {
self.drop_zero()
}
}
impl<
A: AssetId,
B: Balance,
OnDrop: HandleImbalanceDrop<A, B>,
OppositeOnDrop: HandleImbalanceDrop<A, B>,
> Imbalance<A, B, OnDrop, OppositeOnDrop>
{
pub fn zero(asset: A) -> Self {
Self { asset, amount: Zero::zero(), _phantom: PhantomData }
}
pub(crate) fn new(asset: A, amount: B) -> Self {
Self { asset, amount, _phantom: PhantomData }
}
pub fn drop_zero(self) -> Result<(), Self> {
if self.amount.is_zero() {
sp_std::mem::forget(self);
Ok(())
} else {
Err(self)
}
}
pub fn split(self, amount: B) -> (Self, Self) {
let first = self.amount.min(amount);
let second = self.amount - first;
let asset = self.asset.clone();
sp_std::mem::forget(self);
(Imbalance::new(asset.clone(), first), Imbalance::new(asset, second))
}
pub fn merge(mut self, other: Self) -> Result<Self, (Self, Self)> {
if self.asset == other.asset {
self.amount = self.amount.saturating_add(other.amount);
sp_std::mem::forget(other);
Ok(self)
} else {
Err((self, other))
}
}
pub fn subsume(&mut self, other: Self) -> Result<(), Self> {
if self.asset == other.asset {
self.amount = self.amount.saturating_add(other.amount);
sp_std::mem::forget(other);
Ok(())
} else {
Err(other)
}
}
pub fn offset(
self,
other: Imbalance<A, B, OppositeOnDrop, OnDrop>,
) -> Result<
SameOrOther<Self, Imbalance<A, B, OppositeOnDrop, OnDrop>>,
(Self, Imbalance<A, B, OppositeOnDrop, OnDrop>),
> {
if self.asset == other.asset {
let (a, b) = (self.amount, other.amount);
let asset = self.asset.clone();
sp_std::mem::forget((self, other));
if a == b {
Ok(SameOrOther::None)
} else if a > b {
Ok(SameOrOther::Same(Imbalance::new(asset, a - b)))
} else {
Ok(SameOrOther::Other(Imbalance::<A, B, OppositeOnDrop, OnDrop>::new(asset, b - a)))
}
} else {
Err((self, other))
}
}
pub fn peek(&self) -> B {
self.amount
}
pub fn asset(&self) -> A {
self.asset.clone()
}
}
pub type Debt<AccountId, B> = Imbalance<
<B as Inspect<AccountId>>::AssetId,
<B as Inspect<AccountId>>::Balance,
<B as Balanced<AccountId>>::OnDropDebt,
<B as Balanced<AccountId>>::OnDropCredit,
>;
pub type Credit<AccountId, B> = Imbalance<
<B as Inspect<AccountId>>::AssetId,
<B as Inspect<AccountId>>::Balance,
<B as Balanced<AccountId>>::OnDropCredit,
<B as Balanced<AccountId>>::OnDropDebt,
>;