From 93037b184570fd52ff111c232c92baf2da1d4475 Mon Sep 17 00:00:00 2001 From: 448 OG Date: Sat, 13 Apr 2024 17:24:59 +0300 Subject: [PATCH] Respect precision while formatting Amount All characters beyond the needed precision are truncated In the case of a SignedAmount the default precision is now 8 just like the Amount Just like the existing code, no width is added if there exists fractional values exist just like the existing code works. Tests have been improved to handle precision Resolves: #2136 --- units/src/amount.rs | 60 ++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/units/src/amount.rs b/units/src/amount.rs index 94cfaca3e9..fe2717103b 100644 --- a/units/src/amount.rs +++ b/units/src/amount.rs @@ -670,7 +670,7 @@ fn split_amount_and_denomination(s: &str) -> Result<(&str, Denomination), ParseE } /// Options given by `fmt::Formatter` -struct FormatOptions { +pub struct FormatOptions { fill: char, align: Option, width: Option, @@ -821,9 +821,17 @@ fn fmt_satoshi_in( if total_decimals > 0 { write!(f, ".")?; } - if norm_nb_decimals > 0 { + + if let Some(f_precision) = options.precision { + if f_precision < norm_nb_decimals && norm_nb_decimals > 0 { + write!(f, "{:0width$}", 0, width = f_precision)?; + } else if norm_nb_decimals > 0 { + write!(f, "{:0width$}", num_after_decimal_point, width = norm_nb_decimals)?; + } + } else if norm_nb_decimals > 0 { write!(f, "{:0width$}", num_after_decimal_point, width = norm_nb_decimals)?; } + repeat_char(f, '0', trailing_decimal_zeros)?; if show_denom { @@ -1349,8 +1357,8 @@ impl SignedAmount { /// /// Does not include the denomination. #[rustfmt::skip] - pub fn fmt_value_in(self, f: &mut dyn fmt::Write, denom: Denomination) -> fmt::Result { - fmt_satoshi_in(self.unsigned_abs().to_sat(), self.is_negative(), f, denom, false, FormatOptions::default()) + pub fn fmt_value_in(self, f: &mut dyn fmt::Write, denom: Denomination, options: FormatOptions) -> fmt::Result { + fmt_satoshi_in(self.unsigned_abs().to_sat(), self.is_negative(), f, denom, false, options) } /// Get a string number of this [SignedAmount] in the given denomination. @@ -1359,7 +1367,7 @@ impl SignedAmount { #[cfg(feature = "alloc")] pub fn to_string_in(self, denom: Denomination) -> String { let mut buf = String::new(); - self.fmt_value_in(&mut buf, denom).unwrap(); + self.fmt_value_in(&mut buf, denom, FormatOptions::default()).unwrap(); buf } @@ -1368,7 +1376,7 @@ impl SignedAmount { #[cfg(feature = "alloc")] pub fn to_string_with_denomination(self, denom: Denomination) -> String { let mut buf = String::new(); - self.fmt_value_in(&mut buf, denom).unwrap(); + self.fmt_value_in(&mut buf, denom, FormatOptions::default()).unwrap(); write!(buf, " {}", denom).unwrap(); buf } @@ -1476,7 +1484,10 @@ impl fmt::Debug for SignedAmount { // Just using Bitcoin denominated string. impl fmt::Display for SignedAmount { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.fmt_value_in(f, Denomination::Bitcoin)?; + let mut format_options = FormatOptions::default(); + format_options.precision.replace(f.precision().unwrap_or(8)); + self.fmt_value_in(f, Denomination::Bitcoin, format_options)?; + write!(f, " {}", Denomination::Bitcoin) } } @@ -2415,10 +2426,10 @@ mod tests { btc_check_fmt_non_negative_6, 1, "{}", "0.00000001"; btc_check_fmt_non_negative_7, 1, "{:2}", "0.00000001"; btc_check_fmt_non_negative_8, 1, "{:02}", "0.00000001"; - btc_check_fmt_non_negative_9, 1, "{:.1}", "0.00000001"; + btc_check_fmt_non_negative_9, 1, "{:.1}", "0.0"; btc_check_fmt_non_negative_10, 1, "{:11}", " 0.00000001"; - btc_check_fmt_non_negative_11, 1, "{:11.1}", " 0.00000001"; - btc_check_fmt_non_negative_12, 1, "{:011.1}", "00.00000001"; + btc_check_fmt_non_negative_11, 1, "{:11.1}", " 0.0"; + btc_check_fmt_non_negative_12, 1, "{:011.1}", "00.0"; btc_check_fmt_non_negative_13, 1, "{:.9}", "0.000000010"; btc_check_fmt_non_negative_14, 1, "{:11.9}", "0.000000010"; btc_check_fmt_non_negative_15, 1, "{:011.9}", "0.000000010"; @@ -2433,7 +2444,7 @@ mod tests { btc_check_fmt_non_negative_24, 110_000_000, "{}", "1.1"; btc_check_fmt_non_negative_25, 100_000_001, "{}", "1.00000001"; btc_check_fmt_non_negative_26, 100_000_001, "{:1}", "1.00000001"; - btc_check_fmt_non_negative_27, 100_000_001, "{:.1}", "1.00000001"; + btc_check_fmt_non_negative_27, 100_000_001, "{:.1}", "1.0"; btc_check_fmt_non_negative_28, 100_000_001, "{:10}", "1.00000001"; btc_check_fmt_non_negative_29, 100_000_001, "{:11}", " 1.00000001"; btc_check_fmt_non_negative_30, 100_000_001, "{:011}", "01.00000001"; @@ -2465,7 +2476,7 @@ mod tests { check_format_non_negative_show_denom! { Bitcoin, " BTC"; - btc_check_fmt_non_negative_show_denom_0, 1, "{:14.1}", "0.00000001"; + btc_check_fmt_non_negative_show_denom_0, 1, "{:14.1}", "0.0"; btc_check_fmt_non_negative_show_denom_1, 1, "{:14.8}", "0.00000001"; btc_check_fmt_non_negative_show_denom_2, 1, "{:15}", " 0.00000001"; btc_check_fmt_non_negative_show_denom_3, 1, "{:015}", "00.00000001"; @@ -2940,18 +2951,21 @@ mod tests { assert_eq!(format!("{}", Amount::ONE_BTC), "1 BTC"); assert_eq!(format!("{}", Amount::from_sat(1)), "0.00000001 BTC"); assert_eq!(format!("{}", Amount::from_sat(10)), "0.00000010 BTC"); - assert_eq!(format!("{:.2}", Amount::from_sat(10)), "0.0000001 BTC"); - assert_eq!(format!("{:.2}", Amount::from_sat(100)), "0.000001 BTC"); - assert_eq!(format!("{:.2}", Amount::from_sat(1000)), "0.00001 BTC"); - assert_eq!(format!("{:.2}", Amount::from_sat(10_000)), "0.0001 BTC"); - assert_eq!(format!("{:.2}", Amount::from_sat(100_000)), "0.001 BTC"); - assert_eq!(format!("{:.2}", Amount::from_sat(1_000_000)), "0.01 BTC"); - assert_eq!(format!("{:.2}", Amount::from_sat(10_000_000)), "0.10 BTC"); + assert_eq!(format!("{:.1}", Amount::from_sat(10)), "0.0 BTC"); + assert_eq!(format!("{:.2}", Amount::from_sat(10)), "0.00 BTC"); + assert_eq!(format!("{:.3}", Amount::from_sat(10)), "0.000 BTC"); + assert_eq!(format!("{:.4}", Amount::from_sat(10)), "0.0000 BTC"); + assert_eq!(format!("{:.5}", Amount::from_sat(10)), "0.00000 BTC"); + assert_eq!(format!("{:.6}", Amount::from_sat(10)), "0.000000 BTC"); + assert_eq!(format!("{:.7}", Amount::from_sat(10)), "0.0000001 BTC"); + assert_eq!(format!("{:.8}", Amount::from_sat(10)), "0.00000010 BTC"); + assert_eq!(format!("{:.9}", Amount::from_sat(10)), "0.000000100 BTC"); + assert_eq!(format!("{:.1}", Amount::from_sat(100_000_000)), "1.0 BTC"); assert_eq!(format!("{:.2}", Amount::from_sat(100_000_000)), "1.00 BTC"); assert_eq!(format!("{}", Amount::from_sat(100_000_000)), "1 BTC"); - assert_eq!(format!("{}", Amount::from_sat(40_000_000_000)), "400 BTC"); - assert_eq!(format!("{:.10}", Amount::from_sat(100_000_000)), "1.0000000000 BTC"); - assert_eq!(format!("{}", Amount::from_sat(400_000_000_000_010)), "4000000.00000010 BTC"); - assert_eq!(format!("{}", Amount::from_sat(400_000_000_000_000)), "4000000 BTC"); + assert_eq!(format!("{}", Amount::from_sat(40_000_000_001)), "400.00000001 BTC"); + assert_eq!(format!("{}", Amount::from_sat(40_000_000_010)), "400.00000010 BTC"); + assert_eq!(format!("{:.7}", Amount::from_sat(40_000_000_010)), "400.0000001 BTC"); + assert_eq!(format!("{:.1}", Amount::from_sat(40_000_000_001)), "400.0 BTC"); } }