modcli/output/
progress.rs1use std::io::{stdout, Write};
2use std::thread;
3use std::time::Duration;
4
5
6#[derive(Clone)]
8pub struct ProgressStyle {
9 pub fill: char,
10 pub done_label: &'static str,
11}
12
13impl Default for ProgressStyle {
14 fn default() -> Self {
15 Self {
16 fill: '#',
17 done_label: "Done!",
18 }
19 }
20}
21
22pub struct ProgressBar {
24 total_steps: usize,
25 label: Option<String>,
26 style: ProgressStyle,
27}
28
29impl ProgressBar {
30 pub fn new(total_steps: usize, style: ProgressStyle) -> Self {
31 Self {
32 total_steps,
33 label: None,
34 style,
35 }
36 }
37
38 pub fn set_label(&mut self, label: &str) {
39 self.label = Some(label.to_string());
40 }
41
42 pub fn start(&self, duration_ms: u64) {
43 let interval = duration_ms / self.total_steps.max(1) as u64;
44 let mut stdout = stdout();
45
46 if let Some(ref label) = self.label {
47 print!("{} [", label);
48 } else {
49 print!("[");
50 }
51
52 stdout.flush().unwrap();
53
54 for _ in 0..self.total_steps {
55 print!("{}", self.style.fill);
56 stdout.flush().unwrap();
57 thread::sleep(Duration::from_millis(interval));
58 }
59
60 println!("] {}", self.style.done_label);
61 }
62}
63
64pub fn show_progress_bar(label: &str, total_steps: usize, duration_ms: u64) {
66 let mut bar = ProgressBar::new(total_steps, ProgressStyle::default());
67 bar.set_label(label);
68 bar.start(duration_ms);
69}
70
71pub fn show_percent_progress(label: &str, percent: usize) {
72 let clamped = percent.clamp(0, 100);
73 print!("\r{}: {:>3}% complete", label, clamped);
74 stdout().flush().unwrap();
75}
76
77pub fn show_spinner(label: &str, cycles: usize, delay_ms: u64) {
78 let spinner = vec!['|', '/', '-', '\\'];
79 let mut stdout = stdout();
80 print!("{} ", label);
81
82 for i in 0..cycles {
83 let frame = spinner[i % spinner.len()];
84 print!("\r{} {}", label, frame);
85 stdout.flush().unwrap();
86 thread::sleep(Duration::from_millis(delay_ms));
87 }
88
89 println!("\r{} ✓", label);
90}