From 2da332e04a7092aa8430f2383b7162775fc27924 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 18 Feb 2025 15:15:49 +0000 Subject: [PATCH] units: introduce impl_op_for_references and use it in three places This macro can generally handle a lot of different cases where we implement "the same trait but on references". We introduce it here and use it in two places. We will use it in many more, but I wanted to make the diff small on this commit, which introduces the actual macro code and might take a bit of reading to understand. You may want to use --color-moved-ws=allow-indentation-change to review this, and the next commit. The next set of changes will mechanically delete other macros that are made redundant by this. --- units/src/amount/result.rs | 31 +++++++++++---------- units/src/internal_macros.rs | 53 ++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 15 deletions(-) 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: