1use crate::render::Context;
13use crate::render::style::Palette;
14
15const CELLS: usize = 10;
20
21#[must_use]
26pub fn ratio(done: u64, total: u64, ctx: &Context) -> String {
27 let numbers = format!("{done} of {total}");
28 if !ctx.is_human() || total == 0 {
29 return numbers;
30 }
31
32 let filled = cells(done, total);
33 let paint = ctx.painter();
34 let colour = if done >= total {
35 Palette::ok()
36 } else {
37 Palette::warn()
38 };
39
40 format!(
41 "{}{} {numbers}",
42 paint.paint(&"▓".repeat(filled), colour),
43 paint.paint(&"░".repeat(CELLS - filled), Palette::label()),
44 )
45}
46
47fn cells(done: u64, total: u64) -> usize {
54 if done >= total {
55 return CELLS;
56 }
57 if done == 0 {
58 return 0;
59 }
60 let cells = u64::try_from(CELLS).unwrap_or(u64::MAX);
61 let filled = usize::try_from(done.saturating_mul(cells) / total.max(1)).unwrap_or(CELLS);
62 filled.clamp(1, CELLS - 1)
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::render::{Audience, Format};
69
70 fn ctx(audience: Audience) -> Context {
71 Context {
72 format: Format::Text,
73 audience,
74 description_lines: None,
75 extra_fields: Vec::new(),
76 width: 80,
77 images: false,
78 inline: crate::render::image::Inline::default(),
79 }
80 }
81
82 #[test]
84 fn a_pipe_gets_the_numbers_and_nothing_else() {
85 assert_eq!(ratio(4, 6, &ctx(Audience::Machine)), "4 of 6");
86 assert_eq!(ratio(0, 0, &ctx(Audience::Machine)), "0 of 0");
87 }
88
89 #[test]
90 fn a_terminal_gets_the_same_numbers_with_a_bar_in_front() {
91 let drawn = ratio(4, 6, &ctx(Audience::Human));
92 assert!(drawn.ends_with("4 of 6"), "{drawn}");
93 assert!(drawn.contains('▓'), "{drawn}");
94 assert!(drawn.contains('░'), "{drawn}");
95 }
96
97 #[test]
99 fn a_ratio_of_nothing_draws_no_bar() {
100 assert_eq!(ratio(0, 0, &ctx(Audience::Human)), "0 of 0");
101 }
102
103 #[test]
104 fn a_full_bar_means_finished_and_only_that() {
105 assert_eq!(cells(6, 6), CELLS);
106 assert_eq!(cells(7, 6), CELLS);
107 assert_eq!(cells(99, 100), CELLS - 1);
109 }
110
111 #[test]
112 fn any_progress_at_all_is_visible() {
113 assert_eq!(cells(0, 100), 0);
114 assert_eq!(cells(1, 100), 1);
115 }
116}