Skip to content

Commit

Permalink
Respect precision while formatting Amount
Browse files Browse the repository at this point in the history
All characters beyond the needed precision are truncated

In the case of a SignedAmount the default precision is now 8
just like the Amount

Replaces self.fmt_value_in() with fmt_satoshi_in() so that
FomartOptions is not made public which would be the requirements
if self.fmt_value_in() is used.

Tests have been improved to handle precision

Resolves: rust-bitcoin#2136
  • Loading branch information
448-OG committed Apr 14, 2024
1 parent 52080a0 commit 2d8f028
Showing 1 changed file with 41 additions and 21 deletions.
62 changes: 41 additions & 21 deletions units/src/amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<fmt::Alignment>,
width: Option<usize>,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -1350,7 +1358,7 @@ 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())
fmt_satoshi_in(self.unsigned_abs().to_sat(), self.is_negative(), f, denom, false, FormatOptions::default())
}

/// Get a string number of this [SignedAmount] in the given denomination.
Expand Down Expand Up @@ -1476,8 +1484,17 @@ 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)?;
write!(f, " {}", Denomination::Bitcoin)
let mut format_options = FormatOptions::default();
format_options.precision.replace(f.precision().unwrap_or(8));

fmt_satoshi_in(
self.unsigned_abs().to_sat(),
self.is_negative(),
f,
Denomination::Bitcoin,
false,
format_options,
)
}
}

Expand Down Expand Up @@ -2415,10 +2432,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";
Expand All @@ -2433,7 +2450,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";
Expand Down Expand Up @@ -2465,7 +2482,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";
Expand Down Expand Up @@ -2940,18 +2957,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");
}
}

0 comments on commit 2d8f028

Please sign in to comment.