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    ///
21    /// # Panics
22    ///
23    /// Panics if the progress template string is invalid (compile-time constant).
24    #[must_use]
25    pub fn stage_spinner(&self, message: &str) -> ProgressBar {
26        if !self.enabled {
27            return ProgressBar::hidden();
28        }
29
30        let pb = self.multi.add(ProgressBar::new_spinner());
31        pb.set_style(
32            ProgressStyle::with_template("{spinner:.cyan} {msg}")
33                .expect("valid progress template")
34                .tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ "),
35        );
36        pb.set_message(message.to_string());
37        pb.enable_steady_tick(std::time::Duration::from_millis(80));
38        pb
39    }
40
41    /// Create a progress bar for file processing.
42    ///
43    /// # Panics
44    ///
45    /// Panics if the progress template string is invalid (compile-time constant).
46    #[must_use]
47    pub fn file_progress(&self, total: u64, message: &str) -> ProgressBar {
48        if !self.enabled {
49            return ProgressBar::hidden();
50        }
51
52        let pb = self.multi.add(ProgressBar::new(total));
53        pb.set_style(
54            ProgressStyle::with_template(
55                "{spinner:.cyan} {msg} [{bar:30.cyan/dim}] {pos}/{len} ({eta})",
56            )
57            .expect("valid progress template")
58            .progress_chars("━━╸━"),
59        );
60        pb.set_message(message.to_string());
61        pb
62    }
63
64    /// Finish all progress bars.
65    pub fn finish(&self) {
66        let _ = self.multi.clear();
67    }
68}
69
70impl Default for AnalysisProgress {
71    fn default() -> Self {
72        Self::new(false)
73    }
74}