Skip to main content

theater_cli/output/
progress.rs

1use indicatif::{ProgressBar as IndicatifBar, ProgressStyle};
2use std::time::Duration;
3
4use crate::output::Theme;
5
6/// A progress bar wrapper with consistent styling
7#[derive(Debug)]
8pub struct ProgressBar {
9    bar: IndicatifBar,
10    _theme: Theme,
11}
12
13impl ProgressBar {
14    pub fn new(len: u64, theme: Theme) -> Self {
15        let bar = IndicatifBar::new(len);
16
17        // Set a nice style
18        let style = ProgressStyle::default_bar()
19            .template(
20                "{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos:>7}/{len:7} {msg}",
21            )
22            .unwrap()
23            .progress_chars("██░");
24
25        bar.set_style(style);
26        bar.enable_steady_tick(Duration::from_millis(100));
27
28        Self { bar, _theme: theme }
29    }
30
31    /// Create an indeterminate progress bar (spinner)
32    pub fn new_spinner(theme: Theme) -> Self {
33        let bar = IndicatifBar::new_spinner();
34
35        let style = ProgressStyle::default_spinner()
36            .template("{spinner:.green} {elapsed_precise} {msg}")
37            .unwrap()
38            .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]);
39
40        bar.set_style(style);
41        bar.enable_steady_tick(Duration::from_millis(100));
42
43        Self { bar, _theme: theme }
44    }
45
46    /// Set the current position
47    pub fn set_position(&self, pos: u64) {
48        self.bar.set_position(pos);
49    }
50
51    /// Increment the position
52    pub fn inc(&self, delta: u64) {
53        self.bar.inc(delta);
54    }
55
56    /// Set the message
57    pub fn set_message(&self, message: &str) {
58        self.bar.set_message(message.to_string());
59    }
60
61    /// Finish the progress bar with a message
62    pub fn finish_with_message(&self, message: &str) {
63        self.bar.finish_with_message(message.to_string());
64    }
65
66    /// Finish the progress bar
67    pub fn finish(&self) {
68        self.bar.finish();
69    }
70
71    /// Abandon the progress bar (useful for error cases)
72    pub fn abandon(&self) {
73        self.bar.abandon();
74    }
75
76    /// Check if the progress bar is finished
77    pub fn is_finished(&self) -> bool {
78        self.bar.is_finished()
79    }
80}
81
82impl Drop for ProgressBar {
83    fn drop(&mut self) {
84        if !self.is_finished() {
85            self.abandon();
86        }
87    }
88}