Merge rust-bitcoin/rust-bitcoin#2462: feat: implement TryFrom trait to `SignedAmount` and `Amount`

251579f4ef feat: implement TryFrom trait to SignedAmount and Amount (Sumit Kumar)

Pull request description:

  Closes: #2245

  Adds `TryFrom<SignedAmount> for Amount` and `TryFrom<Amount> for Amount` in units module

ACKs for top commit:
  tcharding:
    ACK 251579f4ef
  Kixunil:
    ACK 251579f4ef
  apoelstra:
    ACK 251579f4ef

Tree-SHA512: 3e58d7a891019ccd272417eadc977037167439e3385a7b47c060fe93eba9c47fc37cdc0ca5551174ef2d93b256b2804ad7c01f5f2470ef9e9b7b912877aed11c
This commit is contained in:
Andrew Poelstra 2024-02-12 22:04:19 +00:00
commit 8f7cc4d6b3
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
1 changed files with 53 additions and 3 deletions

View File

@ -865,9 +865,9 @@ impl Amount {
pub fn checked_rem(self, rhs: u64) -> Option<Amount> { self.0.checked_rem(rhs).map(Amount) } pub fn checked_rem(self, rhs: u64) -> Option<Amount> { self.0.checked_rem(rhs).map(Amount) }
/// Convert to a signed amount. /// Convert to a signed amount.
pub fn to_signed(self) -> Result<SignedAmount, ParseAmountError> { pub fn to_signed(self) -> Result<SignedAmount, OutOfRangeError> {
if self.to_sat() > SignedAmount::MAX.to_sat() as u64 { if self.to_sat() > SignedAmount::MAX.to_sat() as u64 {
Err(ParseAmountError::OutOfRange(OutOfRangeError::too_big(true))) Err(OutOfRangeError::too_big(true))
} else { } else {
Ok(SignedAmount::from_sat(self.to_sat() as i64)) Ok(SignedAmount::from_sat(self.to_sat() as i64))
} }
@ -957,6 +957,14 @@ impl FromStr for Amount {
fn from_str(s: &str) -> Result<Self, Self::Err> { Amount::from_str_with_denomination(s) } fn from_str(s: &str) -> Result<Self, Self::Err> { Amount::from_str_with_denomination(s) }
} }
impl TryFrom<SignedAmount> for Amount {
type Error = OutOfRangeError;
fn try_from(value: SignedAmount) -> Result<Self, Self::Error> {
value.to_unsigned()
}
}
impl core::iter::Sum for Amount { impl core::iter::Sum for Amount {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
let sats: u64 = iter.map(|amt| amt.0).sum(); let sats: u64 = iter.map(|amt| amt.0).sum();
@ -1340,6 +1348,14 @@ impl FromStr for SignedAmount {
fn from_str(s: &str) -> Result<Self, Self::Err> { SignedAmount::from_str_with_denomination(s) } fn from_str(s: &str) -> Result<Self, Self::Err> { SignedAmount::from_str_with_denomination(s) }
} }
impl TryFrom<Amount> for SignedAmount {
type Error = OutOfRangeError;
fn try_from(value: Amount) -> Result<Self, Self::Error> {
value.to_signed()
}
}
impl core::iter::Sum for SignedAmount { impl core::iter::Sum for SignedAmount {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
let sats: i64 = iter.map(|amt| amt.0).sum(); let sats: i64 = iter.map(|amt| amt.0).sum();
@ -1710,7 +1726,7 @@ mod verification {
if n1 <= i64::MAX as u64 { if n1 <= i64::MAX as u64 {
Ok(SignedAmount::from_sat(n1.try_into().unwrap())) Ok(SignedAmount::from_sat(n1.try_into().unwrap()))
} else { } else {
Err(ParseAmountError::OutOfRange(OutOfRangeError::too_big(true))) Err(OutOfRangeError::too_big(true))
}, },
); );
} }
@ -1841,6 +1857,40 @@ mod tests {
#[test] #[test]
fn from_int_btc_panic() { Amount::from_int_btc(u64::MAX); } fn from_int_btc_panic() { Amount::from_int_btc(u64::MAX); }
#[test]
fn test_signed_amount_try_from_amount() {
let ua_positive = Amount::from_sat(123);
let sa_positive = SignedAmount::try_from(ua_positive).unwrap();
assert_eq!(sa_positive, SignedAmount(123));
let ua_max = Amount::MAX;
let result = SignedAmount::try_from(ua_max);
assert_eq!(
result,
Err(OutOfRangeError {
is_signed: true,
is_greater_than_max: true
})
);
}
#[test]
fn test_amount_try_from_signed_amount() {
let sa_positive = SignedAmount(123);
let ua_positive = Amount::try_from(sa_positive).unwrap();
assert_eq!(ua_positive, Amount::from_sat(123));
let sa_negative = SignedAmount(-123);
let result = Amount::try_from(sa_negative);
assert_eq!(
result,
Err(OutOfRangeError {
is_signed: false,
is_greater_than_max: false
})
);
}
#[test] #[test]
fn mul_div() { fn mul_div() {
let sat = Amount::from_sat; let sat = Amount::from_sat;