// SPDX-License-Identifier: CC0-1.0 //! Provides a monodic numeric result type that is used to return the result of //! doing mathematical operations (`core::ops`) on amount types. use core::{fmt, ops}; use NumOpResult as R; use super::{Amount, SignedAmount}; /// Result of an operation on [`Amount`] or [`SignedAmount`]. /// /// The type parameter `T` should be normally `Amout` or `SignedAmount`. #[derive(Debug, Clone, PartialEq, Eq)] #[must_use] pub enum NumOpResult { /// Result of a successful mathematical operation. Valid(T), /// Result of an unsuccessful mathematical operation. Error(NumOpError), } impl NumOpResult { /// Returns the contained valid amount, consuming `self`. /// /// # Panics /// /// Panics with `msg` if the numeric result is an `Error`. #[track_caller] pub fn expect(self, msg: &str) -> T { match self { R::Valid(amount) => amount, R::Error(_) => panic!("{}", msg), } } /// Returns the contained valid amount, consuming `self`. /// /// # Panics /// /// Panics if the numeric result is an `Error`. #[track_caller] pub fn unwrap(self) -> T { match self { R::Valid(amount) => amount, R::Error(e) => panic!("tried to unwrap an invalid numeric result: {:?}", e), } } /// Returns the contained error, consuming `self`. /// /// # Panics /// /// Panics if the numeric result is a valid amount. #[track_caller] pub fn unwrap_err(self) -> NumOpError { match self { R::Error(e) => e, R::Valid(a) => panic!("tried to unwrap a valid numeric result: {:?}", a), } } /// Converts this `NumOpResult` to an `Option`. pub fn ok(self) -> Option { match self { R::Valid(amount) => Some(amount), R::Error(_) => None, } } /// Converts this `NumOpResult` to a `Result`. #[allow(clippy::missing_errors_doc)] pub fn into_result(self) -> Result { match self { R::Valid(amount) => Ok(amount), R::Error(e) => Err(e), } } /// Calls `op` if the numeric result is `Valid`, otherwise returns the `Error` value of `self`. pub fn and_then(self, op: F) -> NumOpResult where F: FnOnce(T) -> NumOpResult, { match self { R::Valid(amount) => op(amount), R::Error(e) => R::Error(e), } } /// Returns `true` if the numeric result is a valid amount. pub fn is_valid(&self) -> bool { match self { R::Valid(_) => true, R::Error(_) => false, } } /// Returns `true` if the numeric result is an invalid amount. pub fn is_error(&self) -> bool { !self.is_valid() } } impl From for NumOpResult { fn from(a: Amount) -> Self { Self::Valid(a) } } impl From<&Amount> for NumOpResult { fn from(a: &Amount) -> Self { Self::Valid(*a) } } impl From for NumOpResult { fn from(a: SignedAmount) -> Self { Self::Valid(a) } } impl From<&SignedAmount> for NumOpResult { fn from(a: &SignedAmount) -> Self { Self::Valid(*a) } } impl ops::Add for Amount { type Output = NumOpResult; fn add(self, rhs: Amount) -> Self::Output { self.checked_add(rhs).valid_or_error() } } crate::internal_macros::impl_add_for_amount_references!(Amount); impl ops::Add> for Amount { type Output = NumOpResult; fn add(self, rhs: NumOpResult) -> Self::Output { rhs.and_then(|a| a + self) } } impl ops::Add> for &Amount { type Output = NumOpResult; fn add(self, rhs: NumOpResult) -> Self::Output { rhs.and_then(|a| a + self) } } impl ops::Add for NumOpResult { type Output = NumOpResult; fn add(self, rhs: Amount) -> Self::Output { rhs + self } } impl ops::Add<&Amount> for NumOpResult { type Output = NumOpResult; fn add(self, rhs: &Amount) -> Self::Output { rhs + self } } impl ops::Sub for Amount { type Output = NumOpResult; fn sub(self, rhs: Amount) -> Self::Output { self.checked_sub(rhs).valid_or_error() } } crate::internal_macros::impl_sub_for_amount_references!(Amount); impl ops::Sub> for Amount { type Output = NumOpResult; fn sub(self, rhs: NumOpResult) -> Self::Output { match rhs { R::Valid(amount) => self - amount, R::Error(_) => rhs, } } } impl ops::Sub> for &Amount { type Output = NumOpResult; fn sub(self, rhs: NumOpResult) -> Self::Output { match rhs { R::Valid(amount) => self - amount, R::Error(_) => rhs, } } } impl ops::Sub for NumOpResult { type Output = NumOpResult; fn sub(self, rhs: Amount) -> Self::Output { match self { R::Valid(amount) => amount - rhs, R::Error(_) => self, } } } impl ops::Sub<&Amount> for NumOpResult { type Output = NumOpResult; fn sub(self, rhs: &Amount) -> Self::Output { match self { R::Valid(amount) => amount - (*rhs), R::Error(_) => self, } } } impl ops::Mul for Amount { type Output = NumOpResult; fn mul(self, rhs: u64) -> Self::Output { self.checked_mul(rhs).valid_or_error() } } impl ops::Mul<&u64> for Amount { type Output = NumOpResult; fn mul(self, rhs: &u64) -> Self::Output { self.mul(*rhs) } } impl ops::Mul for &Amount { type Output = NumOpResult; fn mul(self, rhs: u64) -> Self::Output { (*self).mul(rhs) } } impl ops::Mul<&u64> for &Amount { type Output = NumOpResult; fn mul(self, rhs: &u64) -> Self::Output { self.mul(*rhs) } } impl ops::Div for Amount { type Output = NumOpResult; fn div(self, rhs: u64) -> Self::Output { self.checked_div(rhs).valid_or_error() } } impl ops::Div<&u64> for Amount { type Output = NumOpResult; fn div(self, rhs: &u64) -> Self::Output { self.div(*rhs) } } impl ops::Div for &Amount { type Output = NumOpResult; fn div(self, rhs: u64) -> Self::Output { (*self).div(rhs) } } impl ops::Div<&u64> for &Amount { type Output = NumOpResult; fn div(self, rhs: &u64) -> Self::Output { (*self).div(*rhs) } } impl ops::Rem for Amount { type Output = NumOpResult; fn rem(self, modulus: u64) -> Self::Output { self.checked_rem(modulus).valid_or_error() } } impl ops::Rem<&u64> for Amount { type Output = NumOpResult; fn rem(self, modulus: &u64) -> Self::Output { self.rem(*modulus) } } impl ops::Rem for &Amount { type Output = NumOpResult; fn rem(self, modulus: u64) -> Self::Output { (*self).rem(modulus) } } impl ops::Rem<&u64> for &Amount { type Output = NumOpResult; fn rem(self, modulus: &u64) -> Self::Output { (*self).rem(*modulus) } } impl ops::Add for SignedAmount { type Output = NumOpResult; fn add(self, rhs: SignedAmount) -> Self::Output { self.checked_add(rhs).valid_or_error() } } crate::internal_macros::impl_add_for_amount_references!(SignedAmount); impl ops::Add> for SignedAmount { type Output = NumOpResult; fn add(self, rhs: NumOpResult) -> Self::Output { rhs.and_then(|a| a + self) } } impl ops::Add> for &SignedAmount { type Output = NumOpResult; fn add(self, rhs: NumOpResult) -> Self::Output { rhs.and_then(|a| a + self) } } impl ops::Add for NumOpResult { type Output = NumOpResult; fn add(self, rhs: SignedAmount) -> Self::Output { rhs + self } } impl ops::Add<&SignedAmount> for NumOpResult { type Output = NumOpResult; fn add(self, rhs: &SignedAmount) -> Self::Output { rhs + self } } impl ops::Sub for SignedAmount { type Output = NumOpResult; fn sub(self, rhs: SignedAmount) -> Self::Output { self.checked_sub(rhs).valid_or_error() } } crate::internal_macros::impl_sub_for_amount_references!(SignedAmount); impl ops::Sub> for SignedAmount { type Output = NumOpResult; fn sub(self, rhs: NumOpResult) -> Self::Output { match rhs { R::Valid(amount) => amount - rhs, R::Error(_) => rhs, } } } impl ops::Sub> for &SignedAmount { type Output = NumOpResult; fn sub(self, rhs: NumOpResult) -> Self::Output { match rhs { R::Valid(amount) => amount - rhs, R::Error(_) => rhs, } } } impl ops::Sub for NumOpResult { type Output = NumOpResult; fn sub(self, rhs: SignedAmount) -> Self::Output { match self { R::Valid(amount) => amount - rhs, R::Error(_) => self, } } } impl ops::Sub<&SignedAmount> for NumOpResult { type Output = NumOpResult; fn sub(self, rhs: &SignedAmount) -> Self::Output { match self { R::Valid(amount) => amount - *rhs, R::Error(_) => self, } } } impl ops::Mul for SignedAmount { type Output = NumOpResult; fn mul(self, rhs: i64) -> Self::Output { self.checked_mul(rhs).valid_or_error() } } impl ops::Mul<&i64> for SignedAmount { type Output = NumOpResult; fn mul(self, rhs: &i64) -> Self::Output { self.mul(*rhs) } } impl ops::Mul for &SignedAmount { type Output = NumOpResult; fn mul(self, rhs: i64) -> Self::Output { (*self).mul(rhs) } } impl ops::Mul<&i64> for &SignedAmount { type Output = NumOpResult; fn mul(self, rhs: &i64) -> Self::Output { self.mul(*rhs) } } impl ops::Div for SignedAmount { type Output = NumOpResult; fn div(self, rhs: i64) -> Self::Output { self.checked_div(rhs).valid_or_error() } } impl ops::Div<&i64> for SignedAmount { type Output = NumOpResult; fn div(self, rhs: &i64) -> Self::Output { self.div(*rhs) } } impl ops::Div for &SignedAmount { type Output = NumOpResult; fn div(self, rhs: i64) -> Self::Output { (*self).div(rhs) } } impl ops::Div<&i64> for &SignedAmount { type Output = NumOpResult; fn div(self, rhs: &i64) -> Self::Output { (*self).div(*rhs) } } impl ops::Rem for SignedAmount { type Output = NumOpResult; fn rem(self, modulus: i64) -> Self::Output { self.checked_rem(modulus).valid_or_error() } } impl ops::Rem<&i64> for SignedAmount { type Output = NumOpResult; fn rem(self, modulus: &i64) -> Self::Output { self.rem(*modulus) } } impl ops::Rem for &SignedAmount { type Output = NumOpResult; fn rem(self, modulus: i64) -> Self::Output { (*self).rem(modulus) } } impl ops::Rem<&i64> for &SignedAmount { type Output = NumOpResult; fn rem(self, modulus: &i64) -> Self::Output { (*self).rem(*modulus) } } impl ops::Neg for SignedAmount { type Output = Self; fn neg(self) -> Self::Output { Self::from_sat(self.to_sat().neg()) } } impl ops::Add for NumOpResult where T: ops::Add>, { type Output = NumOpResult; fn add(self, rhs: Self) -> Self::Output { match (self, rhs) { (R::Valid(lhs), R::Valid(rhs)) => lhs + rhs, (_, _) => R::Error(NumOpError {}), } } } impl ops::Add> for &NumOpResult where T: ops::Add> + Copy, { type Output = NumOpResult; fn add(self, rhs: NumOpResult) -> Self::Output { match (self, rhs) { (R::Valid(lhs), R::Valid(rhs)) => *lhs + rhs, (_, _) => R::Error(NumOpError {}), } } } impl ops::Add<&NumOpResult> for NumOpResult where T: ops::Add> + Copy, { type Output = NumOpResult; fn add(self, rhs: &NumOpResult) -> Self::Output { match (self, rhs) { (R::Valid(lhs), R::Valid(rhs)) => lhs + *rhs, (_, _) => R::Error(NumOpError {}), } } } impl ops::Add for &NumOpResult where T: ops::Add> + Copy, { type Output = NumOpResult; fn add(self, rhs: &NumOpResult) -> Self::Output { match (self, rhs) { (R::Valid(lhs), R::Valid(rhs)) => *lhs + *rhs, (_, _) => R::Error(NumOpError {}), } } } impl ops::Sub for NumOpResult where T: ops::Sub>, { type Output = NumOpResult; fn sub(self, rhs: Self) -> Self::Output { match (self, rhs) { (R::Valid(lhs), R::Valid(rhs)) => lhs - rhs, (_, _) => R::Error(NumOpError {}), } } } impl ops::Sub> for &NumOpResult where T: ops::Sub> + Copy, { type Output = NumOpResult; fn sub(self, rhs: NumOpResult) -> Self::Output { match (self, rhs) { (R::Valid(lhs), R::Valid(rhs)) => *lhs - rhs, (_, _) => R::Error(NumOpError {}), } } } impl ops::Sub<&NumOpResult> for NumOpResult where T: ops::Sub> + Copy, { type Output = NumOpResult; fn sub(self, rhs: &NumOpResult) -> Self::Output { match (self, rhs) { (R::Valid(lhs), R::Valid(rhs)) => lhs - *rhs, (_, _) => R::Error(NumOpError {}), } } } impl ops::Sub for &NumOpResult where T: ops::Sub> + Copy, { type Output = NumOpResult; fn sub(self, rhs: Self) -> Self::Output { match (self, rhs) { (R::Valid(lhs), R::Valid(rhs)) => *lhs - *rhs, (_, _) => R::Error(NumOpError {}), } } } impl core::iter::Sum> for NumOpResult { fn sum(iter: I) -> Self where I: Iterator>, { iter.fold(R::Valid(Amount::ZERO), |acc, amount| match (acc, amount) { (R::Valid(lhs), R::Valid(rhs)) => lhs + rhs, (_, _) => R::Error(NumOpError {}), }) } } impl<'a> core::iter::Sum<&'a NumOpResult> for NumOpResult { fn sum(iter: I) -> Self where I: Iterator>, { iter.fold(R::Valid(Amount::ZERO), |acc, amount| match (acc, amount) { (R::Valid(lhs), R::Valid(rhs)) => lhs + rhs, (_, _) => R::Error(NumOpError {}), }) } } impl core::iter::Sum> for NumOpResult { fn sum(iter: I) -> Self where I: Iterator>, { iter.fold(R::Valid(SignedAmount::ZERO), |acc, amount| match (acc, amount) { (R::Valid(lhs), R::Valid(rhs)) => lhs + rhs, (_, _) => R::Error(NumOpError {}), }) } } impl<'a> core::iter::Sum<&'a NumOpResult> for NumOpResult { fn sum(iter: I) -> Self where I: Iterator>, { iter.fold(R::Valid(SignedAmount::ZERO), |acc, amount| match (acc, amount) { (R::Valid(lhs), R::Valid(rhs)) => lhs + rhs, (_, _) => R::Error(NumOpError {}), }) } } pub(in crate::amount) trait OptionExt { fn valid_or_error(self) -> NumOpResult; } impl OptionExt for Option { fn valid_or_error(self) -> NumOpResult { match self { Some(amount) => R::Valid(amount), None => R::Error(NumOpError {}), } } } impl OptionExt for Option { fn valid_or_error(self) -> NumOpResult { match self { Some(amount) => R::Valid(amount), None => R::Error(NumOpError {}), } } } /// An error occurred while doing a mathematical operation. #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] pub struct NumOpError; impl fmt::Display for NumOpError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "a math operation on amounts gave an invalid numeric result") } } #[cfg(feature = "std")] impl std::error::Error for NumOpError {}