bitcoin_payment_instructions/
amount.rs1use core::fmt;
8
9#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
16pub struct Amount(u64);
18
19impl fmt::Debug for Amount {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
21 write!(f, "{} milli-satoshis", self.0)
22 }
23}
24
25impl Amount {
26 #[inline]
28 pub const fn msats(&self) -> u64 {
29 self.0
30 }
31
32 #[inline]
34 pub const fn sats(&self) -> Result<u64, ()> {
35 if self.0 % 1000 == 0 {
36 Ok(self.0 / 1000)
37 } else {
38 Err(())
39 }
40 }
41
42 #[inline]
44 pub const fn sats_rounding_up(&self) -> u64 {
45 (self.0 + 999) / 1000
46 }
47
48 #[inline]
50 pub const fn from_milli_sats(msats: u64) -> Self {
51 Amount(msats)
52 }
53
54 #[inline]
56 pub const fn from_sats(sats: u64) -> Self {
57 Amount(sats * 1000)
58 }
59
60 #[inline]
62 pub fn saturating_add(self, rhs: Amount) -> Amount {
63 match self.0.checked_add(rhs.0) {
64 Some(amt) if amt <= 21_000_000_0000_0000_000 => Amount(amt),
65 _ => Amount(21_000_000_0000_0000_000),
66 }
67 }
68
69 #[inline]
73 pub fn btc_decimal_rounding_up_to_sats(self) -> FormattedAmount {
74 FormattedAmount(self)
75 }
76}
77
78#[derive(Clone, Copy)]
79pub struct FormattedAmount(Amount);
82
83impl fmt::Display for FormattedAmount {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
85 let total_sats = self.0.sats_rounding_up();
86 let btc = total_sats / 1_0000_0000;
87 let mut sats = total_sats % 1_0000_0000;
88 write!(f, "{}", btc)?;
89 if sats != 0 {
90 let mut digits = 8;
91 while sats % 10 == 0 {
92 digits -= 1;
93 sats /= 10;
94 }
95 write!(f, ".{:0digits$}", sats, digits = digits)?;
96 }
97 Ok(())
98 }
99}
100
101#[cfg(test)]
102mod test {
103 use super::Amount;
104
105 use alloc::string::ToString;
106
107 #[test]
108 #[rustfmt::skip]
109 fn test_display() {
110 assert_eq!(Amount::from_milli_sats(0).btc_decimal_rounding_up_to_sats().to_string(), "0");
111 assert_eq!(Amount::from_milli_sats(1).btc_decimal_rounding_up_to_sats().to_string(), "0.00000001");
112 assert_eq!(Amount::from_sats(1).btc_decimal_rounding_up_to_sats().to_string(), "0.00000001");
113 assert_eq!(Amount::from_sats(10).btc_decimal_rounding_up_to_sats().to_string(), "0.0000001");
114 assert_eq!(Amount::from_sats(15).btc_decimal_rounding_up_to_sats().to_string(), "0.00000015");
115 assert_eq!(Amount::from_sats(1_0000).btc_decimal_rounding_up_to_sats().to_string(), "0.0001");
116 assert_eq!(Amount::from_sats(1_2345).btc_decimal_rounding_up_to_sats().to_string(), "0.00012345");
117 assert_eq!(Amount::from_sats(1_2345_6789).btc_decimal_rounding_up_to_sats().to_string(), "1.23456789");
118 assert_eq!(Amount::from_sats(1_0000_0000).btc_decimal_rounding_up_to_sats().to_string(), "1");
119 assert_eq!(Amount::from_sats(5_0000_0000).btc_decimal_rounding_up_to_sats().to_string(), "5");
120 }
121}