From dc04d638f1dd1e7aa50efa088c1d100e2bad1fe6 Mon Sep 17 00:00:00 2001 From: Mateusz Kwapich Date: Wed, 21 May 2025 14:06:17 +0100 Subject: [PATCH 1/4] [style] demonstrate the rounding problem in percentage rendering --- src/style.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/style.rs b/src/style.rs index dc40ca1a..cead30ed 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1055,4 +1055,21 @@ mod tests { assert_eq!(&buf[2], "bar"); assert_eq!(&buf[3], "baz"); } + + #[test] + fn test_pct_precision() { + // seeing something that's not finished as 100% hurts my eyes + const WIDTH: u16 = 80; + let pos = Arc::new(AtomicPosition::new()); + pos.set(358); + let state = ProgressState::new(Some(359), pos); + + let style = ProgressStyle::default_bar() + .template( + &format!("{{pos}}/{{len}} {{percent}}%"), + ).unwrap(); + let mut buf = Vec::new(); + style.format_state(&state, &mut buf, WIDTH); + assert_eq!(&buf[0], "358/359 100%"); + } } From 247e4d01adad0bc9f4e26231fed96c7cda9871bd Mon Sep 17 00:00:00 2001 From: Mateusz Kwapich Date: Wed, 21 May 2025 14:21:14 +0100 Subject: [PATCH 2/4] [style] 358/359 is NOT 100% By default rust float formatting doesn't round down - in fact they use round_ties_even udner the hood: https://doc.rust-lang.org/std/primitive.f32.html#method.round_ties_even For percentage calculations this is not great - in particular we shouldn't show 100% when the operation is not fully complete. --- src/style.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/style.rs b/src/style.rs index cead30ed..533b0cc7 100644 --- a/src/style.rs +++ b/src/style.rs @@ -302,7 +302,7 @@ impl ProgressStyle { buf.write_fmt(format_args!("{}", HumanCount(len))).unwrap(); } "percent" => buf - .write_fmt(format_args!("{:.*}", 0, state.fraction() * 100f32)) + .write_fmt(format_args!("{:.*}", 0, (state.fraction() * 100f32).floor())) .unwrap(), "percent_precise" => buf .write_fmt(format_args!("{:.*}", 3, state.fraction() * 100f32)) @@ -1062,7 +1062,7 @@ mod tests { const WIDTH: u16 = 80; let pos = Arc::new(AtomicPosition::new()); pos.set(358); - let state = ProgressState::new(Some(359), pos); + let state = ProgressState::new(Some(359), pos.clone()); let style = ProgressStyle::default_bar() .template( @@ -1070,6 +1070,10 @@ mod tests { ).unwrap(); let mut buf = Vec::new(); style.format_state(&state, &mut buf, WIDTH); - assert_eq!(&buf[0], "358/359 100%"); + assert_eq!(&buf[0], "358/359 99%"); + buf.clear(); + pos.set(359); + style.format_state(&state, &mut buf, WIDTH); + assert_eq!(&buf[0], "359/359 100%"); } } From fdcaa673343b1f22b1c131cba2daa1077e693569 Mon Sep 17 00:00:00 2001 From: Mateusz Kwapich Date: Wed, 21 May 2025 18:38:58 +0100 Subject: [PATCH 3/4] fix format --- src/style.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/style.rs b/src/style.rs index 533b0cc7..907b1155 100644 --- a/src/style.rs +++ b/src/style.rs @@ -302,7 +302,11 @@ impl ProgressStyle { buf.write_fmt(format_args!("{}", HumanCount(len))).unwrap(); } "percent" => buf - .write_fmt(format_args!("{:.*}", 0, (state.fraction() * 100f32).floor())) + .write_fmt(format_args!( + "{:.*}", + 0, + (state.fraction() * 100f32).floor() + )) .unwrap(), "percent_precise" => buf .write_fmt(format_args!("{:.*}", 3, state.fraction() * 100f32)) @@ -1065,9 +1069,8 @@ mod tests { let state = ProgressState::new(Some(359), pos.clone()); let style = ProgressStyle::default_bar() - .template( - &format!("{{pos}}/{{len}} {{percent}}%"), - ).unwrap(); + .template(&format!("{{pos}}/{{len}} {{percent}}%")) + .unwrap(); let mut buf = Vec::new(); style.format_state(&state, &mut buf, WIDTH); assert_eq!(&buf[0], "358/359 99%"); From ac58e514012d3cd978bab4e092569dcfd1b72f3d Mon Sep 17 00:00:00 2001 From: Mateusz Kwapich Date: Wed, 21 May 2025 20:08:24 +0100 Subject: [PATCH 4/4] [style] show that percent_precise also shows 100% on incomplete operations --- src/style.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/style.rs b/src/style.rs index 907b1155..44f59e11 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1079,4 +1079,23 @@ mod tests { style.format_state(&state, &mut buf, WIDTH); assert_eq!(&buf[0], "359/359 100%"); } + + #[test] + fn test_pct_precise_precision() { + const WIDTH: u16 = 80; + let pos = Arc::new(AtomicPosition::new()); + pos.set(999999); + let state = ProgressState::new(Some(1000000), pos.clone()); + + let style = ProgressStyle::default_bar() + .template(&format!("{{pos}}/{{len}} {{percent_precise}}%")) + .unwrap(); + let mut buf = Vec::new(); + style.format_state(&state, &mut buf, WIDTH); + assert_eq!(&buf[0], "999999/1000000 100.000%"); + buf.clear(); + pos.set(1000000); + style.format_state(&state, &mut buf, WIDTH); + assert_eq!(&buf[0], "1000000/1000000 100.000%"); + } }