Merge rust-bitcoin/rust-bitcoin#4076: units: macroize the op implementations

814685e551 Macroise the NumOpResult tests (Tobin C. Harding)
a44a9d31f6 Add a few impls to the result macro (Andrew Poelstra)
353c23fa01 units: pull generic op impls on NumOpResult into macro (Andrew Poelstra)
21ac5aefe0 units: extend op reference macro to handle generics and where clauses (Andrew Poelstra)
ad9564895b units: replace a gazillion more macro calls with new macro (Andrew Poelstra)
0dc7f6cebd units: rearrange a bit of code to prep the next commit (Andrew Poelstra)
a358e79a85 units: allow multiple invocations in impl_op_for_references macro (Andrew Poelstra)
2da332e04a units: introduce impl_op_for_references and use it in three places (Andrew Poelstra)
c90559de8e Derive Copy for NumOpResult (Tobin C. Harding)

Pull request description:

  This introduces a general macro which takes some number of `impl ops::Whatever<A> for <B>` and replicates them with all the permutations of references. It also takes a syntax which resembles the code for the initial impl block.

  Uses it for all the binary opcodes on `Amount`, `SignedAmount`, `Weight`, `FeeRate`, as well as the numeric mul/div on those types, as well as generic impls on `NumResultOp<T>` (which are expanded to cover more cases).

ACKs for top commit:
  tcharding:
    ACK 814685e551

Tree-SHA512: 6b2ec389373ad50af6681f18fa13b3892bdee953541e6b854d187e6f49ec5e924aae812d4f0ad121f1ff34566161ebea3ce34f2aa87f3f3bda74a5af970add70
This commit is contained in:
merge-script 2025-02-27 16:46:08 +00:00
commit d889767273
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
5 changed files with 342 additions and 663 deletions

View File

@ -12,7 +12,7 @@ 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)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[must_use]
pub enum NumOpResult<T> {
/// Result of a successful mathematical operation.
@ -123,283 +123,178 @@ 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() }
}
crate::internal_macros::impl_add_for_amount_references!(Amount);
fn add(self, rhs: Amount) -> Self::Output { self.checked_add(rhs).valid_or_error() }
}
impl ops::Add<NumOpResult<Amount>> for Amount {
type Output = NumOpResult<Amount>;
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>;
impl ops::Sub<Amount> for Amount {
type Output = NumOpResult<Amount>;
fn add(self, rhs: NumOpResult<Amount>) -> Self::Output { rhs.and_then(|a| a + self) }
}
impl ops::Add<Amount> for NumOpResult<Amount> {
type Output = NumOpResult<Amount>;
fn sub(self, rhs: Amount) -> Self::Output { self.checked_sub(rhs).valid_or_error() }
}
impl ops::Sub<NumOpResult<Amount>> for Amount {
type Output = NumOpResult<Amount>;
fn add(self, rhs: Amount) -> Self::Output { rhs + self }
}
impl ops::Add<&Amount> for NumOpResult<Amount> {
type Output = NumOpResult<Amount>;
fn add(self, rhs: &Amount) -> Self::Output { rhs + self }
}
impl ops::Sub for Amount {
type Output = NumOpResult<Amount>;
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>;
fn sub(self, rhs: NumOpResult<Amount>) -> Self::Output {
match rhs {
R::Valid(amount) => self - amount,
R::Error(_) => rhs,
fn sub(self, rhs: NumOpResult<Amount>) -> Self::Output {
match rhs {
R::Valid(amount) => self - amount,
R::Error(_) => rhs,
}
}
}
}
impl ops::Sub<NumOpResult<Amount>> for &Amount {
type Output = NumOpResult<Amount>;
fn sub(self, rhs: NumOpResult<Amount>) -> Self::Output {
match rhs {
R::Valid(amount) => self - amount,
R::Error(_) => rhs,
impl ops::Mul<u64> for Amount {
type Output = NumOpResult<Amount>;
fn mul(self, rhs: u64) -> Self::Output { self.checked_mul(rhs).valid_or_error() }
}
impl ops::Mul<u64> for NumOpResult<Amount> {
type Output = NumOpResult<Amount>;
fn mul(self, rhs: u64) -> Self::Output { self.and_then(|lhs| lhs * rhs) }
}
impl ops::Div<u64> for Amount {
type Output = NumOpResult<Amount>;
fn div(self, rhs: u64) -> Self::Output { self.checked_div(rhs).valid_or_error() }
}
impl ops::Div<u64> for NumOpResult<Amount> {
type Output = NumOpResult<Amount>;
fn div(self, rhs: u64) -> Self::Output { self.and_then(|lhs| lhs / rhs) }
}
impl ops::Rem<u64> for Amount {
type Output = NumOpResult<Amount>;
fn rem(self, modulus: u64) -> Self::Output { self.checked_rem(modulus).valid_or_error() }
}
impl ops::Rem<u64> for NumOpResult<Amount> {
type Output = NumOpResult<Amount>;
fn rem(self, modulus: u64) -> Self::Output { self.and_then(|lhs| lhs % modulus) }
}
impl ops::Add<SignedAmount> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn add(self, rhs: SignedAmount) -> Self::Output { self.checked_add(rhs).valid_or_error() }
}
impl ops::Add<NumOpResult<SignedAmount>> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn add(self, rhs: NumOpResult<SignedAmount>) -> Self::Output { rhs.and_then(|a| a + self) }
}
impl ops::Sub<SignedAmount> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn sub(self, rhs: SignedAmount) -> Self::Output { self.checked_sub(rhs).valid_or_error() }
}
impl ops::Sub<NumOpResult<SignedAmount>> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn sub(self, rhs: NumOpResult<SignedAmount>) -> Self::Output {
match rhs {
R::Valid(amount) => self - amount,
R::Error(_) => rhs,
}
}
}
}
impl ops::Sub<Amount> for NumOpResult<Amount> {
type Output = NumOpResult<Amount>;
fn sub(self, rhs: Amount) -> Self::Output {
match self {
R::Valid(amount) => amount - rhs,
R::Error(_) => self,
impl ops::Mul<i64> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn mul(self, rhs: i64) -> Self::Output { self.checked_mul(rhs).valid_or_error() }
}
impl ops::Mul<i64> for NumOpResult<SignedAmount> {
type Output = NumOpResult<SignedAmount>;
fn mul(self, rhs: i64) -> Self::Output { self.and_then(|lhs| lhs * rhs) }
}
impl ops::Div<i64> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn div(self, rhs: i64) -> Self::Output { self.checked_div(rhs).valid_or_error() }
}
impl ops::Div<i64> for NumOpResult<SignedAmount> {
type Output = NumOpResult<SignedAmount>;
fn div(self, rhs: i64) -> Self::Output { self.and_then(|lhs| lhs / rhs) }
}
impl ops::Rem<i64> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn rem(self, modulus: i64) -> Self::Output { self.checked_rem(modulus).valid_or_error() }
}
impl ops::Rem<i64> for NumOpResult<SignedAmount> {
type Output = NumOpResult<SignedAmount>;
fn rem(self, modulus: i64) -> Self::Output { self.and_then(|lhs| lhs % modulus) }
}
impl<T> ops::Add<NumOpResult<T>> for NumOpResult<T>
where
(T: Copy + ops::Add<Output = NumOpResult<T>>)
{
type Output = NumOpResult<T>;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(R::Valid(lhs), R::Valid(rhs)) => lhs + rhs,
(_, _) => R::Error(NumOpError {}),
}
}
}
}
impl ops::Sub<&Amount> for NumOpResult<Amount> {
type Output = NumOpResult<Amount>;
fn sub(self, rhs: &Amount) -> Self::Output {
match self {
R::Valid(amount) => amount - (*rhs),
R::Error(_) => self,
impl<T> ops::Add<T> for NumOpResult<T>
where
(T: Copy + ops::Add<NumOpResult<T>, Output = NumOpResult<T>>)
{
type Output = NumOpResult<T>;
fn add(self, rhs: T) -> Self::Output { rhs + self }
}
impl<T> ops::Sub<NumOpResult<T>> for NumOpResult<T>
where
(T: Copy + ops::Sub<Output = NumOpResult<T>>)
{
type Output = NumOpResult<T>;
fn sub(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(R::Valid(lhs), R::Valid(rhs)) => lhs - rhs,
(_, _) => R::Error(NumOpError {}),
}
}
}
}
impl ops::Mul<u64> for Amount {
type Output = NumOpResult<Amount>;
impl<T> ops::Sub<T> for NumOpResult<T>
where
(T: Copy + ops::Sub<Output = NumOpResult<T>>)
{
type Output = NumOpResult<T>;
fn mul(self, rhs: u64) -> Self::Output { self.checked_mul(rhs).valid_or_error() }
}
impl ops::Mul<&u64> for Amount {
type Output = NumOpResult<Amount>;
fn mul(self, rhs: &u64) -> Self::Output { self.mul(*rhs) }
}
impl ops::Mul<u64> for &Amount {
type Output = NumOpResult<Amount>;
fn mul(self, rhs: u64) -> Self::Output { (*self).mul(rhs) }
}
impl ops::Mul<&u64> for &Amount {
type Output = NumOpResult<Amount>;
fn mul(self, rhs: &u64) -> Self::Output { self.mul(*rhs) }
}
impl ops::Div<u64> for Amount {
type Output = NumOpResult<Amount>;
fn div(self, rhs: u64) -> Self::Output { self.checked_div(rhs).valid_or_error() }
}
impl ops::Div<&u64> for Amount {
type Output = NumOpResult<Amount>;
fn div(self, rhs: &u64) -> Self::Output { self.div(*rhs) }
}
impl ops::Div<u64> for &Amount {
type Output = NumOpResult<Amount>;
fn div(self, rhs: u64) -> Self::Output { (*self).div(rhs) }
}
impl ops::Div<&u64> for &Amount {
type Output = NumOpResult<Amount>;
fn div(self, rhs: &u64) -> Self::Output { (*self).div(*rhs) }
}
impl ops::Rem<u64> for Amount {
type Output = NumOpResult<Amount>;
fn rem(self, modulus: u64) -> Self::Output { self.checked_rem(modulus).valid_or_error() }
}
impl ops::Rem<&u64> for Amount {
type Output = NumOpResult<Amount>;
fn rem(self, modulus: &u64) -> Self::Output { self.rem(*modulus) }
}
impl ops::Rem<u64> for &Amount {
type Output = NumOpResult<Amount>;
fn rem(self, modulus: u64) -> Self::Output { (*self).rem(modulus) }
}
impl ops::Rem<&u64> for &Amount {
type Output = NumOpResult<Amount>;
fn rem(self, modulus: &u64) -> Self::Output { (*self).rem(*modulus) }
}
impl ops::Add for SignedAmount {
type Output = NumOpResult<SignedAmount>;
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<NumOpResult<SignedAmount>> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn add(self, rhs: NumOpResult<SignedAmount>) -> Self::Output { rhs.and_then(|a| a + self) }
}
impl ops::Add<NumOpResult<SignedAmount>> for &SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn add(self, rhs: NumOpResult<SignedAmount>) -> Self::Output { rhs.and_then(|a| a + self) }
}
impl ops::Add<SignedAmount> for NumOpResult<SignedAmount> {
type Output = NumOpResult<SignedAmount>;
fn add(self, rhs: SignedAmount) -> Self::Output { rhs + self }
}
impl ops::Add<&SignedAmount> for NumOpResult<SignedAmount> {
type Output = NumOpResult<SignedAmount>;
fn add(self, rhs: &SignedAmount) -> Self::Output { rhs + self }
}
impl ops::Sub for SignedAmount {
type Output = NumOpResult<SignedAmount>;
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<NumOpResult<SignedAmount>> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn sub(self, rhs: NumOpResult<SignedAmount>) -> Self::Output {
match rhs {
R::Valid(amount) => amount - rhs,
R::Error(_) => rhs,
fn sub(self, rhs: T) -> Self::Output {
match self {
R::Valid(amount) => amount - rhs,
R::Error(_) => self,
}
}
}
}
impl ops::Sub<NumOpResult<SignedAmount>> for &SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn sub(self, rhs: NumOpResult<SignedAmount>) -> Self::Output {
match rhs {
R::Valid(amount) => amount - rhs,
R::Error(_) => rhs,
}
}
}
impl ops::Sub<SignedAmount> for NumOpResult<SignedAmount> {
type Output = NumOpResult<SignedAmount>;
fn sub(self, rhs: SignedAmount) -> Self::Output {
match self {
R::Valid(amount) => amount - rhs,
R::Error(_) => self,
}
}
}
impl ops::Sub<&SignedAmount> for NumOpResult<SignedAmount> {
type Output = NumOpResult<SignedAmount>;
fn sub(self, rhs: &SignedAmount) -> Self::Output {
match self {
R::Valid(amount) => amount - *rhs,
R::Error(_) => self,
}
}
}
impl ops::Mul<i64> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn mul(self, rhs: i64) -> Self::Output { self.checked_mul(rhs).valid_or_error() }
}
impl ops::Mul<&i64> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn mul(self, rhs: &i64) -> Self::Output { self.mul(*rhs) }
}
impl ops::Mul<i64> for &SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn mul(self, rhs: i64) -> Self::Output { (*self).mul(rhs) }
}
impl ops::Mul<&i64> for &SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn mul(self, rhs: &i64) -> Self::Output { self.mul(*rhs) }
}
impl ops::Div<i64> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn div(self, rhs: i64) -> Self::Output { self.checked_div(rhs).valid_or_error() }
}
impl ops::Div<&i64> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn div(self, rhs: &i64) -> Self::Output { self.div(*rhs) }
}
impl ops::Div<i64> for &SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn div(self, rhs: i64) -> Self::Output { (*self).div(rhs) }
}
impl ops::Div<&i64> for &SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn div(self, rhs: &i64) -> Self::Output { (*self).div(*rhs) }
}
impl ops::Rem<i64> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn rem(self, modulus: i64) -> Self::Output { self.checked_rem(modulus).valid_or_error() }
}
impl ops::Rem<&i64> for SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn rem(self, modulus: &i64) -> Self::Output { self.rem(*modulus) }
}
impl ops::Rem<i64> for &SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn rem(self, modulus: i64) -> Self::Output { (*self).rem(modulus) }
}
impl ops::Rem<&i64> for &SignedAmount {
type Output = NumOpResult<SignedAmount>;
fn rem(self, modulus: &i64) -> Self::Output { (*self).rem(*modulus) }
}
impl ops::Neg for SignedAmount {
type Output = Self;
@ -407,112 +302,6 @@ impl ops::Neg for SignedAmount {
fn neg(self) -> Self::Output { Self::from_sat(self.to_sat().neg()) }
}
impl<T> ops::Add for NumOpResult<T>
where
T: ops::Add<Output = NumOpResult<T>>,
{
type Output = NumOpResult<T>;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(R::Valid(lhs), R::Valid(rhs)) => lhs + rhs,
(_, _) => R::Error(NumOpError {}),
}
}
}
impl<T> ops::Add<NumOpResult<T>> for &NumOpResult<T>
where
T: ops::Add<Output = NumOpResult<T>> + Copy,
{
type Output = NumOpResult<T>;
fn add(self, rhs: NumOpResult<T>) -> Self::Output {
match (self, rhs) {
(R::Valid(lhs), R::Valid(rhs)) => *lhs + rhs,
(_, _) => R::Error(NumOpError {}),
}
}
}
impl<T> ops::Add<&NumOpResult<T>> for NumOpResult<T>
where
T: ops::Add<Output = NumOpResult<T>> + Copy,
{
type Output = NumOpResult<T>;
fn add(self, rhs: &NumOpResult<T>) -> Self::Output {
match (self, rhs) {
(R::Valid(lhs), R::Valid(rhs)) => lhs + *rhs,
(_, _) => R::Error(NumOpError {}),
}
}
}
impl<T> ops::Add for &NumOpResult<T>
where
T: ops::Add<Output = NumOpResult<T>> + Copy,
{
type Output = NumOpResult<T>;
fn add(self, rhs: &NumOpResult<T>) -> Self::Output {
match (self, rhs) {
(R::Valid(lhs), R::Valid(rhs)) => *lhs + *rhs,
(_, _) => R::Error(NumOpError {}),
}
}
}
impl<T> ops::Sub for NumOpResult<T>
where
T: ops::Sub<Output = NumOpResult<T>>,
{
type Output = NumOpResult<T>;
fn sub(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(R::Valid(lhs), R::Valid(rhs)) => lhs - rhs,
(_, _) => R::Error(NumOpError {}),
}
}
}
impl<T> ops::Sub<NumOpResult<T>> for &NumOpResult<T>
where
T: ops::Sub<Output = NumOpResult<T>> + Copy,
{
type Output = NumOpResult<T>;
fn sub(self, rhs: NumOpResult<T>) -> Self::Output {
match (self, rhs) {
(R::Valid(lhs), R::Valid(rhs)) => *lhs - rhs,
(_, _) => R::Error(NumOpError {}),
}
}
}
impl<T> ops::Sub<&NumOpResult<T>> for NumOpResult<T>
where
T: ops::Sub<Output = NumOpResult<T>> + Copy,
{
type Output = NumOpResult<T>;
fn sub(self, rhs: &NumOpResult<T>) -> Self::Output {
match (self, rhs) {
(R::Valid(lhs), R::Valid(rhs)) => lhs - *rhs,
(_, _) => R::Error(NumOpError {}),
}
}
}
impl<T> ops::Sub for &NumOpResult<T>
where
T: ops::Sub<Output = NumOpResult<T>> + Copy,
{
type Output = NumOpResult<T>;
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<NumOpResult<Amount>> for NumOpResult<Amount> {
fn sum<I>(iter: I) -> Self
where
@ -584,7 +373,7 @@ impl OptionExt<SignedAmount> for Option<SignedAmount> {
}
/// An error occurred while doing a mathematical operation.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct NumOpError;

View File

@ -1196,184 +1196,133 @@ fn check_const() {
assert_eq!(Amount::MAX_MONEY.to_sat() as i64, SignedAmount::MAX_MONEY.to_sat());
}
// Verify we have implemented all combinations of ops for `Amount` and `SignedAmount`.
// It's easier to read this test that check the code.
// Sanity check than stdlib supports the set of reference combinations for the ops we want.
#[test]
#[allow(clippy::op_ref)] // We are explicitly testing the references work with ops.
fn amount_tyes_all_ops() {
// Sanity check than stdlib supports the set of reference combinations for the ops we want.
{
let x = 127;
fn sanity_all_ops() {
let x = 127;
let _ = x + x;
let _ = &x + x;
let _ = x + &x;
let _ = &x + &x;
let _ = x + x;
let _ = &x + x;
let _ = x + &x;
let _ = &x + &x;
let _ = x - x;
let _ = &x - x;
let _ = x - &x;
let _ = &x - &x;
let _ = x - x;
let _ = &x - x;
let _ = x - &x;
let _ = &x - &x;
let _ = -x;
}
let _ = -x;
}
// Verify we have implemented all combinations of ops for the amount types and `NumOpResult` type.
// It's easier to read this test than check the code.
#[test]
#[allow(clippy::op_ref)] // We are explicitly testing the references work with ops.
fn num_op_result_ops() {
let sat = Amount::from_sat(1);
let ssat = SignedAmount::from_sat(1);
// Add
let _ = sat + sat;
let _ = &sat + sat;
let _ = sat + &sat;
let _ = &sat + &sat;
// Explicit type as sanity check.
let res: NumOpResult<Amount> = sat + sat;
let sres: NumOpResult<SignedAmount> = ssat + ssat;
// let _ = ssat + sat;
// let _ = &ssat + sat;
// let _ = ssat + &sat;
// let _ = &ssat + &sat;
macro_rules! check_op {
($(let _ = $lhs:ident $op:tt $rhs:ident);* $(;)?) => {
$(
let _ = $lhs $op $rhs;
let _ = &$lhs $op $rhs;
let _ = $lhs $op &$rhs;
let _ = &$lhs $op &$rhs;
)*
}
}
// let _ = sat + ssat;
// let _ = &sat + ssat;
// let _ = sat + &ssat;
// let _ = &sat + &ssat;
// We do not currently support division involving `NumOpResult` and an amount type.
check_op! {
// Operations where RHS is the result of another operation.
let _ = sat + res;
let _ = sat - res;
// let _ = sat / res;
let _ = ssat + sres;
let _ = ssat - sres;
// let _ = ssat / sres;
let _ = ssat + ssat;
let _ = &ssat + ssat;
let _ = ssat + &ssat;
let _ = &ssat + &ssat;
// Operations where LHS is the result of another operation.
let _ = res + sat;
let _ = res - sat;
// let _ = res / sat;
let _ = sres + ssat;
let _ = sres - ssat;
// let _ = sres / ssat;
// Sub
let _ = sat - sat;
let _ = &sat - sat;
let _ = sat - &sat;
let _ = &sat - &sat;
// let _ = ssat - sat;
// let _ = &ssat - sat;
// let _ = ssat - &sat;
// let _ = &ssat - &sat;
// let _ = sat - ssat;
// let _ = &sat - ssat;
// let _ = sat - &ssat;
// let _ = &sat - &ssat;
let _ = ssat - ssat;
let _ = &ssat - ssat;
let _ = ssat - &ssat;
let _ = &ssat - &ssat;
// let _ = sat * sat; // Intentionally not supported.
// Mul
let _ = sat * 3;
let _ = sat * &3;
let _ = &sat * 3;
let _ = &sat * &3;
let _ = ssat * 3_i64; // Explicit type for the benefit of the reader.
let _ = ssat * &3;
let _ = &ssat * 3;
let _ = &ssat * &3;
// Div
let _ = sat / 3;
let _ = &sat / 3;
let _ = sat / &3;
let _ = &sat / &3;
let _ = ssat / 3_i64; // Explicit type for the benefit of the reader.
let _ = &ssat / 3;
let _ = ssat / &3;
let _ = &ssat / &3;
// Rem
let _ = sat % 3;
let _ = &sat % 3;
let _ = sat % &3;
let _ = &sat % &3;
let _ = ssat % 3;
let _ = &ssat % 3;
let _ = ssat % &3;
let _ = &ssat % &3;
// FIXME: Do we want to support this?
// let _ = sat / sat;
//
// "How many times does this amount go into that amount?" - seems
// like a reasonable question to ask.
// FIXME: Do we want to support these?
// let _ = -sat;
// let _ = -ssat;
// Operations that where both sides are the result of another operation.
let _ = res + res;
let _ = res - res;
// let _ = res / res;
let _ = sres + sres;
let _ = sres - sres;
// let _ = sres / sres;
};
}
// FIXME: Should we support this sort of thing?
// It will be a lot more code for possibly not that much benefit.
#[test]
fn can_ops_on_amount_and_signed_amount() {
// let res: NumOpResult<SignedAmount> = sat + ssat;
}
// Verify we have implemented all combinations of ops for the `NumOpResult` type.
// It's easier to read this test that check the code.
// Verify we have implemented all combinations of ops for the `NumOpResult` type and an integer.
// It's easier to read this test than check the code.
#[test]
#[allow(clippy::op_ref)] // We are explicitly testing the references work with ops.
fn amount_op_result_all_ops() {
fn num_op_result_ops_integer() {
let sat = Amount::from_sat(1);
// let ssat = SignedAmount::from_sat(1);
let ssat = SignedAmount::from_sat(1);
// Explicit type as sanity check.
let res: NumOpResult<Amount> = sat + sat;
// let sres: NumOpResult<SignedAmount> = ssat + ssat;
let sres: NumOpResult<SignedAmount> = ssat + ssat;
// Operations that where RHS is the result of another operation.
let _ = sat + res.clone();
let _ = &sat + res.clone();
// let _ = sat + &res.clone();
// let _ = &sat + &res.clone();
macro_rules! check_op {
($(let _ = $lhs:ident $op:tt $rhs:literal);* $(;)?) => {
$(
let _ = $lhs $op $rhs;
let _ = &$lhs $op $rhs;
let _ = $lhs $op &$rhs;
let _ = &$lhs $op &$rhs;
)*
}
}
check_op! {
// Operations on a `NumOpResult` and integer.
let _ = res * 3_u64; // Explicit type for the benefit of the reader.
let _ = res / 3;
let _ = res % 3;
let _ = sat - res.clone();
let _ = &sat - res.clone();
// let _ = sat - &res.clone();
// let _ = &sat - &res.clone();
let _ = sres * 3_i64; // Explicit type for the benefit of the reader.
let _ = sres / 3;
let _ = sres % 3;
};
}
// Operations that where LHS is the result of another operation.
let _ = res.clone() + sat;
// let _ = &res.clone() + sat;
let _ = res.clone() + &sat;
// let _ = &res.clone() + &sat;
// Verify we have implemented all `Neg` for the amount types.
#[test]
fn amount_op_result_neg() {
// TODO: Implement Neg all round.
let _ = res.clone() - sat;
// let _ = &res.clone() - sat;
let _ = res.clone() - &sat;
// let _ = &res.clone() - &sat;
// let sat = Amount::from_sat(1);
let ssat = SignedAmount::from_sat(1);
// Operations that where both sides are the result of another operation.
let _ = res.clone() + res.clone();
// let _ = &res.clone() + res.clone();
// let _ = res.clone() + &res.clone();
// let _ = &res.clone() + &res.clone();
let _ = res.clone() - res.clone();
// let _ = &res.clone() - res.clone();
// let _ = res.clone() - &res.clone();
// let _ = &res.clone() - &res.clone();
// let _ = -sat;
let _ = -ssat;
// let _ = -res;
// let _ = -sres;
}
// Verify we have implemented all `Sum` for the `NumOpResult` type.
#[test]
fn amount_op_result_sum() {
let res = Amount::from_sat(1) + Amount::from_sat(1);
let amounts = [res.clone(), res.clone()];
let amounts = [res, res];
let amount_refs = [&res, &res];
// Sum iterators.
let _ = amounts.iter().sum::<NumOpResult<Amount>>();
let _ = amount_refs.iter().copied().sum::<NumOpResult<Amount>>();
let _ = amount_refs.into_iter().sum::<NumOpResult<Amount>>();
// FIXME: Should we support this? I don't think so (Tobin).
// let _ = amount_refs.iter().sum::<NumOpResult<&Amount>>();
}

View File

@ -135,20 +135,20 @@ impl From<FeeRate> for u64 {
fn from(value: FeeRate) -> Self { value.to_sat_per_kwu() }
}
impl ops::Add for FeeRate {
type Output = FeeRate;
crate::internal_macros::impl_op_for_references! {
impl ops::Add<FeeRate> for FeeRate {
type Output = FeeRate;
fn add(self, rhs: FeeRate) -> Self::Output { FeeRate(self.0 + rhs.0) }
fn add(self, rhs: FeeRate) -> Self::Output { FeeRate(self.0 + rhs.0) }
}
impl ops::Sub<FeeRate> for FeeRate {
type Output = FeeRate;
fn sub(self, rhs: FeeRate) -> Self::Output { FeeRate(self.0 - rhs.0) }
}
}
crate::internal_macros::impl_add_for_references!(FeeRate);
crate::internal_macros::impl_add_assign!(FeeRate);
impl ops::Sub for FeeRate {
type Output = FeeRate;
fn sub(self, rhs: FeeRate) -> Self::Output { FeeRate(self.0 - rhs.0) }
}
crate::internal_macros::impl_sub_for_references!(FeeRate);
crate::internal_macros::impl_sub_assign!(FeeRate);
impl core::iter::Sum for FeeRate {

View File

@ -4,65 +4,70 @@
//!
//! Macros meant to be used inside the `bitcoin-units` library.
/// Implements `ops::Add` for various references.
/// Implements an opcode for various reference combinations.
///
/// Requires `$ty` it implement `Add` e.g. 'impl Add<T> for T'. Adds impls of:
/// 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<T> for &T
/// - Add<&T> for T
/// - Add<&T> for &T
macro_rules! impl_add_for_references {
($ty:ident) => {
impl core::ops::Add<$ty> for &$ty {
type Output = $ty;
fn add(self, rhs: $ty) -> Self::Output { *self + rhs }
/// - `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.
///
/// Your where clause must include extra parenthesis, like `where (T: Copy)`.
macro_rules! impl_op_for_references {
($(
impl$(<$gen:ident>)? $($op_trait:ident)::+<$other_ty:ty> for $ty:ty
$(where ($($bounds:tt)*))?
{
type Output = $($main_output:ty)*;
fn $op:ident($($main_args:tt)*) -> Self::Output {
$($main_impl:tt)*
}
}
)+) => {$(
impl$(<$gen>)? $($op_trait)::+<$other_ty> for $ty
$(where $($bounds)*)?
{
type Output = $($main_output)*;
fn $op($($main_args)*) -> Self::Output {
$($main_impl)*
}
}
impl core::ops::Add<&$ty> for $ty {
type Output = $ty;
fn add(self, rhs: &$ty) -> Self::Output { self + *rhs }
impl$(<$gen>)? $($op_trait)::+<$other_ty> for &$ty
$(where $($bounds)*)?
{
type Output = <$ty as $($op_trait)::+<$other_ty>>::Output;
fn $op(self, rhs: $other_ty) -> Self::Output {
(*self).$op(rhs)
}
}
impl<'a> core::ops::Add<&'a $ty> for &$ty {
type Output = $ty;
fn add(self, rhs: &'a $ty) -> Self::Output { *self + *rhs }
impl$(<$gen>)? $($op_trait)::+<&$other_ty> for $ty
$(where $($bounds)*)?
{
type Output = <$ty as $($op_trait)::+<$other_ty>>::Output;
fn $op(self, rhs: &$other_ty) -> Self::Output {
self.$op(*rhs)
}
}
};
impl<'a, $($gen)?> $($op_trait)::+<&'a $other_ty> for &$ty
$(where $($bounds)*)?
{
type Output = <$ty as $($op_trait)::+<$other_ty>>::Output;
fn $op(self, rhs: &$other_ty) -> Self::Output {
(*self).$op(*rhs)
}
}
)+};
}
pub(crate) use impl_add_for_references;
/// Implements `ops::Add` for various amount references.
///
/// Requires `$ty` it implement `Add` e.g. 'impl Add<T> for T'. Adds impls of:
///
/// - Add<T> for &T
/// - Add<&T> for T
/// - Add<&T> for &T
macro_rules! impl_add_for_amount_references {
($ty:ident) => {
impl core::ops::Add<$ty> for &$ty {
type Output = NumOpResult<$ty>;
fn add(self, rhs: $ty) -> Self::Output { *self + rhs }
}
impl core::ops::Add<&$ty> for $ty {
type Output = NumOpResult<$ty>;
fn add(self, rhs: &$ty) -> Self::Output { self + *rhs }
}
impl<'a> core::ops::Add<&'a $ty> for &$ty {
type Output = NumOpResult<$ty>;
fn add(self, rhs: &'a $ty) -> Self::Output { *self + *rhs }
}
};
}
pub(crate) use impl_add_for_amount_references;
pub(crate) use impl_op_for_references;
/// Implement `ops::AddAssign` for `$ty` and `&$ty`.
macro_rules! impl_add_assign {
@ -78,66 +83,6 @@ macro_rules! impl_add_assign {
}
pub(crate) use impl_add_assign;
/// Implement `ops::Sub` for various references.
///
/// Requires `$ty` it implement `Sub` e.g. 'impl Sub<T> for T'. Adds impls of:
///
/// - Sub<T> for &T
/// - Sub<&T> for T
/// - Sub<&T> for &T
macro_rules! impl_sub_for_references {
($ty:ident) => {
impl core::ops::Sub<$ty> for &$ty {
type Output = $ty;
fn sub(self, rhs: $ty) -> Self::Output { *self - rhs }
}
impl core::ops::Sub<&$ty> for $ty {
type Output = $ty;
fn sub(self, rhs: &$ty) -> Self::Output { self - *rhs }
}
impl<'a> core::ops::Sub<&'a $ty> for &$ty {
type Output = $ty;
fn sub(self, rhs: &'a $ty) -> Self::Output { *self - *rhs }
}
};
}
pub(crate) use impl_sub_for_references;
/// Implement `ops::Sub` for various amount references.
///
/// Requires `$ty` it implement `Sub` e.g. 'impl Sub<T> for T'. Adds impls of:
///
/// - Sub<T> for &T
/// - Sub<&T> for T
/// - Sub<&T> for &T
macro_rules! impl_sub_for_amount_references {
($ty:ident) => {
impl core::ops::Sub<$ty> for &$ty {
type Output = NumOpResult<$ty>;
fn sub(self, rhs: $ty) -> Self::Output { *self - rhs }
}
impl core::ops::Sub<&$ty> for $ty {
type Output = NumOpResult<$ty>;
fn sub(self, rhs: &$ty) -> Self::Output { self - *rhs }
}
impl<'a> core::ops::Sub<&'a $ty> for &$ty {
type Output = NumOpResult<$ty>;
fn sub(self, rhs: &'a $ty) -> Self::Output { *self - *rhs }
}
};
}
pub(crate) use impl_sub_for_amount_references;
/// Implement `ops::SubAssign` for `$ty` and `&$ty`.
macro_rules! impl_sub_assign {
($ty:ident) => {

View File

@ -166,50 +166,46 @@ impl From<Weight> for u64 {
fn from(value: Weight) -> Self { value.to_wu() }
}
impl ops::Add for Weight {
type Output = Weight;
crate::internal_macros::impl_op_for_references! {
impl ops::Add<Weight> for Weight {
type Output = Weight;
fn add(self, rhs: Weight) -> Self::Output { Weight(self.0 + rhs.0) }
fn add(self, rhs: Weight) -> Self::Output { Weight(self.0 + rhs.0) }
}
impl ops::Sub<Weight> for Weight {
type Output = Weight;
fn sub(self, rhs: Weight) -> Self::Output { Weight(self.0 - rhs.0) }
}
impl ops::Mul<u64> for Weight {
type Output = Weight;
fn mul(self, rhs: u64) -> Self::Output { Weight(self.0 * rhs) }
}
impl ops::Mul<Weight> for u64 {
type Output = Weight;
fn mul(self, rhs: Weight) -> Self::Output { Weight(self * rhs.0) }
}
impl ops::Div<u64> for Weight {
type Output = Weight;
fn div(self, rhs: u64) -> Self::Output { Weight(self.0 / rhs) }
}
impl ops::Div<Weight> for Weight {
type Output = u64;
fn div(self, rhs: Weight) -> Self::Output { self.to_wu() / rhs.to_wu() }
}
}
crate::internal_macros::impl_add_for_references!(Weight);
crate::internal_macros::impl_add_assign!(Weight);
impl ops::Sub for Weight {
type Output = Weight;
fn sub(self, rhs: Weight) -> Self::Output { Weight(self.0 - rhs.0) }
}
crate::internal_macros::impl_sub_for_references!(Weight);
crate::internal_macros::impl_sub_assign!(Weight);
impl ops::Mul<u64> for Weight {
type Output = Weight;
fn mul(self, rhs: u64) -> Self::Output { Weight(self.0 * rhs) }
}
impl ops::Mul<Weight> for u64 {
type Output = Weight;
fn mul(self, rhs: Weight) -> Self::Output { Weight(self * rhs.0) }
}
impl ops::MulAssign<u64> for Weight {
fn mul_assign(&mut self, rhs: u64) { self.0 *= rhs }
}
impl ops::Div<u64> for Weight {
type Output = Weight;
fn div(self, rhs: u64) -> Self::Output { Weight(self.0 / rhs) }
}
impl ops::Div<Weight> for Weight {
type Output = u64;
fn div(self, rhs: Weight) -> Self::Output { self.to_wu() / rhs.to_wu() }
}
impl ops::DivAssign<u64> for Weight {
fn div_assign(&mut self, rhs: u64) { self.0 /= rhs }
}