oxur_cli/common/
progress.rs

1//! Progress tracking for long-running operations
2//!
3//! Provides a simple progress tracker that can show numbered steps
4//! with optional verbose output.
5
6use colored::*;
7
8/// A progress tracker for multi-step operations
9///
10/// Tracks the current step and provides methods to display progress
11/// with optional verbose mode.
12///
13/// # Examples
14///
15/// ```no_run
16/// use oxur_cli::common::progress::ProgressTracker;
17///
18/// # fn main() -> anyhow::Result<()> {
19/// let mut progress = ProgressTracker::new(true); // verbose mode
20///
21/// progress.step("Parsing input");
22/// // ... do work ...
23/// progress.done();
24///
25/// progress.step("Generating output");
26/// // ... do work ...
27/// progress.done();
28///
29/// progress.success("All operations completed!");
30/// # Ok(())
31/// # }
32/// ```
33pub struct ProgressTracker {
34    current_step: usize,
35    verbose: bool,
36}
37
38impl ProgressTracker {
39    /// Create a new progress tracker
40    ///
41    /// # Arguments
42    ///
43    /// * `verbose` - If true, shows detailed step-by-step progress
44    pub fn new(verbose: bool) -> Self {
45        Self { current_step: 0, verbose }
46    }
47
48    /// Start a new step in the process
49    ///
50    /// In verbose mode, displays: "N. message..."
51    /// In non-verbose mode, does nothing
52    pub fn step(&mut self, msg: &str) {
53        if self.verbose {
54            self.current_step += 1;
55            println!("{}. {}...", self.current_step, msg);
56        }
57    }
58
59    /// Mark the current step as complete
60    ///
61    /// In verbose mode, displays: "   ✓ Done" (in green)
62    /// In non-verbose mode, does nothing
63    pub fn done(&self) {
64        if self.verbose {
65            println!("   {} Done", "✓".green());
66        }
67    }
68
69    /// Display a final success message
70    ///
71    /// Always displays, regardless of verbose mode.
72    /// In verbose mode, adds a blank line before the message.
73    pub fn success(&self, msg: &str) {
74        if self.verbose {
75            println!();
76        }
77        println!("{} {}", "✓".green().bold(), msg);
78    }
79
80    /// Display an error message
81    ///
82    /// Always displays, regardless of verbose mode.
83    pub fn error(&self, msg: &str) {
84        eprintln!("{} {}", "Error:".red().bold(), msg);
85    }
86
87    /// Display an info message
88    ///
89    /// Only displays in verbose mode.
90    pub fn info(&self, msg: &str) {
91        if self.verbose {
92            println!("{} {}", "→".cyan(), msg);
93        }
94    }
95
96    /// Check if verbose mode is enabled
97    pub fn is_verbose(&self) -> bool {
98        self.verbose
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_new_verbose() {
108        let tracker = ProgressTracker::new(true);
109        assert!(tracker.is_verbose());
110        assert_eq!(tracker.current_step, 0);
111    }
112
113    #[test]
114    fn test_new_non_verbose() {
115        let tracker = ProgressTracker::new(false);
116        assert!(!tracker.is_verbose());
117    }
118
119    #[test]
120    fn test_step_increments_counter() {
121        let mut tracker = ProgressTracker::new(true);
122        assert_eq!(tracker.current_step, 0);
123
124        tracker.step("First step");
125        assert_eq!(tracker.current_step, 1);
126
127        tracker.step("Second step");
128        assert_eq!(tracker.current_step, 2);
129    }
130
131    #[test]
132    fn test_step_non_verbose_no_increment() {
133        let mut tracker = ProgressTracker::new(false);
134        tracker.step("Step");
135        assert_eq!(tracker.current_step, 0);
136    }
137
138    #[test]
139    fn test_done_verbose() {
140        let tracker = ProgressTracker::new(true);
141        tracker.done();
142    }
143
144    #[test]
145    fn test_done_non_verbose() {
146        let tracker = ProgressTracker::new(false);
147        tracker.done();
148    }
149
150    #[test]
151    fn test_success() {
152        let tracker = ProgressTracker::new(true);
153        tracker.success("All done!");
154    }
155
156    #[test]
157    fn test_error() {
158        let tracker = ProgressTracker::new(true);
159        tracker.error("Something failed");
160    }
161
162    #[test]
163    fn test_info_verbose() {
164        let tracker = ProgressTracker::new(true);
165        tracker.info("Additional info");
166    }
167
168    #[test]
169    fn test_info_non_verbose() {
170        let tracker = ProgressTracker::new(false);
171        tracker.info("This shouldn't display");
172    }
173}