Skip to main content

fallow_core/
progress.rs

1use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
2
3/// Progress reporter for analysis stages.
4pub struct AnalysisProgress {
5    multi: MultiProgress,
6    enabled: bool,
7}
8
9impl AnalysisProgress {
10    /// Create a new progress reporter.
11    #[must_use]
12    pub fn new(enabled: bool) -> Self {
13        Self {
14            multi: MultiProgress::new(),
15            enabled,
16        }
17    }
18
19    /// Create a spinner for a stage.
20    #[must_use]
21    pub fn stage_spinner(&self, message: &str) -> ProgressBar {
22        if !self.enabled {
23            return ProgressBar::hidden();
24        }
25
26        let pb = self.multi.add(ProgressBar::new_spinner());
27        pb.set_style(
28            ProgressStyle::with_template("{spinner:.cyan} {msg}")
29                .expect("valid progress template")
30                .tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ "),
31        );
32        pb.set_message(message.to_string());
33        pb.enable_steady_tick(std::time::Duration::from_millis(80));
34        pb
35    }
36
37    /// Create a progress bar for file processing.
38    #[must_use]
39    pub fn file_progress(&self, total: u64, message: &str) -> ProgressBar {
40        if !self.enabled {
41            return ProgressBar::hidden();
42        }
43
44        let pb = self.multi.add(ProgressBar::new(total));
45        pb.set_style(
46            ProgressStyle::with_template(
47                "{spinner:.cyan} {msg} [{bar:30.cyan/dim}] {pos}/{len} ({eta})",
48            )
49            .expect("valid progress template")
50            .progress_chars("━━╸━"),
51        );
52        pb.set_message(message.to_string());
53        pb
54    }
55
56    /// Finish all progress bars.
57    pub fn finish(&self) {
58        let _ = self.multi.clear();
59    }
60}
61
62impl Default for AnalysisProgress {
63    fn default() -> Self {
64        Self::new(false)
65    }
66}