diff --git a/units/src/amount/result.rs b/units/src/amount/result.rs index df26be7c7..a8582a5e1 100644 --- a/units/src/amount/result.rs +++ b/units/src/amount/result.rs @@ -123,23 +123,23 @@ impl From<&SignedAmount> for NumOpResult { fn from(a: &SignedAmount) -> Self { Self::Valid(*a) } } -impl ops::Add for Amount { - type Output = NumOpResult; +crate::internal_macros::impl_op_for_references! { + impl ops::Add for Amount { + type Output = NumOpResult; - fn add(self, rhs: Amount) -> Self::Output { self.checked_add(rhs).valid_or_error() } + 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; +crate::internal_macros::impl_op_for_references! { + impl ops::Add> for Amount { + type Output = NumOpResult; - fn add(self, rhs: NumOpResult) -> Self::Output { rhs.and_then(|a| a + self) } + 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) } -} +// FIXME these two should be covered by generic impls below impl ops::Add for NumOpResult { type Output = NumOpResult; @@ -151,12 +151,13 @@ impl ops::Add<&Amount> for NumOpResult { fn add(self, rhs: &Amount) -> Self::Output { rhs + self } } -impl ops::Sub for Amount { - type Output = NumOpResult; +crate::internal_macros::impl_op_for_references! { + impl ops::Sub for Amount { + type Output = NumOpResult; - fn sub(self, rhs: Amount) -> Self::Output { self.checked_sub(rhs).valid_or_error() } + 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; diff --git a/units/src/internal_macros.rs b/units/src/internal_macros.rs index 1bae68986..d65167518 100644 --- a/units/src/internal_macros.rs +++ b/units/src/internal_macros.rs @@ -4,6 +4,59 @@ //! //! Macros meant to be used inside the `bitcoin-units` library. +/// Implements an opcode for various reference combinations. +/// +/// Given `$ty`, assumes the `$op_trait<$other_ty>` trait is implemented on it, +/// and implements the same trait with the full matrix of `&$ty` and `&$other_ty`: +/// +/// - `Add<$other_ty> for &$ty` +/// - `Add<&$other_ty> for $ty` +/// - `Add<&$other_ty> for &$ty` +/// +/// # Limitations +/// +/// You must specify `$other_ty` and you may not use `Self`. So e.g. you need +/// to write `impl ops::Add for Amount { ... }` when calling this macro. +macro_rules! impl_op_for_references { + ( + impl $($op_trait:ident)::+<$other_ty:ty> for $ty:ty { + type Output = $($main_output:ty)*; + fn $op:ident($($main_args:tt)*) -> Self::Output { + $($main_impl:tt)* + } + } + ) => { + impl $($op_trait)::+<$other_ty> for $ty { + type Output = $($main_output)*; + fn $op($($main_args)*) -> Self::Output { + $($main_impl)* + } + } + + impl $($op_trait)::+<$other_ty> for &$ty { + type Output = <$ty as $($op_trait)::+<$other_ty>>::Output; + fn $op(self, rhs: $other_ty) -> Self::Output { + (*self).$op(rhs) + } + } + + impl $($op_trait)::+<&$other_ty> for $ty { + type Output = <$ty as $($op_trait)::+<$other_ty>>::Output; + fn $op(self, rhs: &$other_ty) -> Self::Output { + self.$op(*rhs) + } + } + + impl<'a> $($op_trait)::+<&'a $other_ty> for &$ty { + type Output = <$ty as $($op_trait)::+<$other_ty>>::Output; + fn $op(self, rhs: &$other_ty) -> Self::Output { + (*self).$op(*rhs) + } + } + }; +} +pub(crate) use impl_op_for_references; + /// Implements `ops::Add` for various references. /// /// Requires `$ty` it implement `Add` e.g. 'impl Add for T'. Adds impls of: