1use std::io::{IsTerminal, Write};
7
8pub struct ProgressBar {
10 total: usize,
11 current: usize,
12 enabled: bool,
13 bar_width: usize,
14}
15
16impl ProgressBar {
17 pub fn new(total: usize, quiet: bool) -> Self {
19 let enabled = !quiet && total > 0 && std::io::stderr().is_terminal();
20 Self {
21 total,
22 current: 0,
23 enabled,
24 bar_width: 20,
25 }
26 }
27
28 pub fn tick(&mut self, file_path: &str) {
30 self.current += 1;
31 if !self.enabled {
32 return;
33 }
34
35 let pct = self.current as f64 / self.total as f64;
36 let filled = (pct * self.bar_width as f64) as usize;
37 let empty = self.bar_width - filled;
38
39 let bar: String = "█".repeat(filled) + &"░".repeat(empty);
40 let counter = format!("{}/{}", self.current, self.total);
41
42 let prefix_width = self.bar_width + 6 + counter.len();
46 let max_path = 80usize.saturating_sub(prefix_width);
47 let display_path = if file_path.len() > max_path && max_path > 3 {
48 let start = file_path
49 .char_indices()
50 .rev()
51 .nth(max_path - 4)
52 .map(|(i, _)| i)
53 .unwrap_or(0);
54 format!("...{}", &file_path[start..])
55 } else {
56 file_path.to_string()
57 };
58
59 eprint!("\r[{bar}] {counter} : {display_path}\x1b[K");
61 let _ = std::io::stderr().flush();
62 }
63
64 pub fn finish(&self) {
66 if self.enabled {
67 eprint!("\r\x1b[K");
68 let _ = std::io::stderr().flush();
69 }
70 }
71}