modcli/output/
progress.rs

1use std::io::{stdout, Write};
2use std::thread;
3use std::time::Duration;
4
5
6/// Style struct for customizing progress bar (expandable)
7#[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
22/// Struct-based progress bar (more control than `show_*` functions)
23pub 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
64/// Simple procedural variants (keep these!)
65pub 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}