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.
This commit is contained in:
Andrew Poelstra 2025-02-18 15:15:49 +00:00
parent c90559de8e
commit 2da332e04a
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
2 changed files with 69 additions and 15 deletions

View File

@ -123,23 +123,23 @@ impl From<&SignedAmount> for NumOpResult<SignedAmount> {
fn from(a: &SignedAmount) -> Self { Self::Valid(*a) }
}
impl ops::Add for Amount {
type Output = NumOpResult<Amount>;
crate::internal_macros::impl_op_for_references! {
impl ops::Add<Amount> for Amount {
type Output = NumOpResult<Amount>;
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<NumOpResult<Amount>> for Amount {
type Output = NumOpResult<Amount>;
crate::internal_macros::impl_op_for_references! {
impl ops::Add<NumOpResult<Amount>> for Amount {
type Output = NumOpResult<Amount>;
fn add(self, rhs: NumOpResult<Amount>) -> Self::Output { rhs.and_then(|a| a + self) }
fn add(self, rhs: NumOpResult<Amount>) -> Self::Output { rhs.and_then(|a| a + self) }
}
}
impl ops::Add<NumOpResult<Amount>> for &Amount {
type Output = NumOpResult<Amount>;
fn add(self, rhs: NumOpResult<Amount>) -> Self::Output { rhs.and_then(|a| a + self) }
}
// FIXME these two should be covered by generic impls below
impl ops::Add<Amount> for NumOpResult<Amount> {
type Output = NumOpResult<Amount>;
@ -151,12 +151,13 @@ impl ops::Add<&Amount> for NumOpResult<Amount> {
fn add(self, rhs: &Amount) -> Self::Output { rhs + self }
}
impl ops::Sub for Amount {
type Output = NumOpResult<Amount>;
crate::internal_macros::impl_op_for_references! {
impl ops::Sub<Amount> for Amount {
type Output = NumOpResult<Amount>;
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<NumOpResult<Amount>> for Amount {
type Output = NumOpResult<Amount>;

View File

@ -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<Amount> 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<T> for T'. Adds impls of: