Implement `CheckedSum` for amount types

It's just `Sum` with checked arithmetic.
This commit is contained in:
Sebastian Geisler 2021-06-12 17:09:49 +02:00
parent f28110b31c
commit 6f7da5f2ef
1 changed files with 75 additions and 0 deletions

View File

@ -863,6 +863,35 @@ impl ::std::iter::Sum for SignedAmount {
} }
} }
/// Calculate the sum over the iterator using checked arithmetic.
pub trait CheckedSum<R> {
/// Calculate the sum over the iterator using checked arithmetic. If an over or underflow would
/// happen it returns `None`.
fn checked_sum(self) -> Option<R>;
}
impl<T> CheckedSum<Amount> for T where T: Iterator<Item = Amount> {
fn checked_sum(mut self) -> Option<Amount> {
let first = Some(self.next().unwrap_or_default());
self.fold(
first,
|acc, item| acc.and_then(|acc| acc.checked_add(item))
)
}
}
impl<T> CheckedSum<SignedAmount> for T where T: Iterator<Item = SignedAmount> {
fn checked_sum(mut self) -> Option<SignedAmount> {
let first = Some(self.next().unwrap_or_default());
self.fold(
first,
|acc, item| acc.and_then(|acc| acc.checked_add(item))
)
}
}
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
pub mod serde { pub mod serde {
// methods are implementation of a standardized serde-specific signature // methods are implementation of a standardized serde-specific signature
@ -1548,4 +1577,50 @@ mod tests {
let sum = amounts.into_iter().sum::<SignedAmount>(); let sum = amounts.into_iter().sum::<SignedAmount>();
assert_eq!(SignedAmount::from_sat(1316), sum); assert_eq!(SignedAmount::from_sat(1316), sum);
} }
#[test]
fn checked_sum_amounts() {
assert_eq!(Some(Amount::from_sat(0)), vec![].into_iter().checked_sum());
assert_eq!(Some(SignedAmount::from_sat(0)), vec![].into_iter().checked_sum());
let amounts = vec![
Amount::from_sat(42),
Amount::from_sat(1337),
Amount::from_sat(21)
];
let sum = amounts.into_iter().checked_sum();
assert_eq!(Some(Amount::from_sat(1400)), sum);
let amounts = vec![
Amount::from_sat(u64::max_value()),
Amount::from_sat(1337),
Amount::from_sat(21)
];
let sum = amounts.into_iter().checked_sum();
assert_eq!(None, sum);
let amounts = vec![
SignedAmount::from_sat(i64::min_value()),
SignedAmount::from_sat(-1),
SignedAmount::from_sat(21)
];
let sum = amounts.into_iter().checked_sum();
assert_eq!(None, sum);
let amounts = vec![
SignedAmount::from_sat(i64::max_value()),
SignedAmount::from_sat(1),
SignedAmount::from_sat(21)
];
let sum = amounts.into_iter().checked_sum();
assert_eq!(None, sum);
let amounts = vec![
SignedAmount::from_sat(42),
SignedAmount::from_sat(3301),
SignedAmount::from_sat(21)
];
let sum = amounts.into_iter().checked_sum();
assert_eq!(Some(SignedAmount::from_sat(3364)), sum);
}
} }