docker_image_pusher/output/
mod.rs

1//! Enhanced output control module with structured logging
2
3use std::io::{self, Write};
4use std::time::{Instant, Duration};
5
6#[derive(Clone, Debug)]
7pub struct OutputManager {
8    pub verbose: bool,
9    quiet: bool,
10    start_time: Option<Instant>,
11}
12
13impl OutputManager {
14    pub fn new(verbose: bool) -> Self {
15        Self {
16            verbose,
17            quiet: false,
18            start_time: Some(Instant::now()),
19        }
20    }
21
22    pub fn new_quiet() -> Self {
23        Self {
24            verbose: false,
25            quiet: true,
26            start_time: Some(Instant::now()),
27        }
28    }
29
30    // Structured logging levels
31    pub fn trace(&self, message: &str) {
32        if self.verbose && !self.quiet {
33            println!("šŸ” TRACE: {}", message);
34        }
35    }
36
37    pub fn debug(&self, message: &str) {
38        if self.verbose && !self.quiet {
39            println!("šŸ› DEBUG: {}", message);
40        }
41    }
42
43    pub fn verbose(&self, message: &str) {
44        if self.verbose && !self.quiet {
45            println!("šŸ“ {}", message);
46        }
47    }
48
49    pub fn info(&self, message: &str) {
50        if !self.quiet {
51            println!("ā„¹ļø  {}", message);
52        }
53    }
54
55    pub fn success(&self, message: &str) {
56        if !self.quiet {
57            println!("āœ… {}", message);
58        }
59    }
60
61    pub fn warning(&self, message: &str) {
62        if !self.quiet {
63            println!("āš ļø  WARNING: {}", message);
64        }
65    }
66
67    pub fn error(&self, message: &str) {
68        eprintln!("āŒ ERROR: {}", message);
69    }
70
71    // Progress indicators
72    pub fn progress(&self, message: &str) {
73        if !self.quiet {
74            print!("ā³ {}...", message);
75            let _ = io::stdout().flush();
76        }
77    }
78
79    pub fn progress_done(&self) {
80        if !self.quiet {
81            println!(" āœ“");
82        }
83    }
84
85    // Section headers
86    pub fn section(&self, title: &str) {
87        if !self.quiet {
88            println!("\nšŸ”§ {}", title);
89            println!("{}", "=".repeat(title.len() + 3));
90        }
91    }
92
93    pub fn subsection(&self, title: &str) {
94        if !self.quiet {
95            println!("\nšŸ“‚ {}", title);
96            println!("{}", "-".repeat(title.len() + 3));
97        }
98    }
99
100    pub fn step(&self, step: &str) {
101        if !self.quiet {
102            println!("  šŸš€ {}", step);
103        }
104    }
105
106    pub fn detail(&self, detail: &str) {
107        if self.verbose && !self.quiet {
108            println!("    šŸ“‹ {}", detail);
109        }
110    }
111
112    // Summary method for displaying structured information
113    pub fn summary(&self, title: &str, items: &[String]) {
114        if !self.quiet {
115            println!("\nšŸ“‹ {}", title);
116            println!("{}", "─".repeat(title.len() + 3));
117            
118            for item in items {
119                println!("  • {}", item);
120            }
121            
122            if items.is_empty() {
123                println!("  (No items to display)");
124            }
125        }
126    }
127
128    // Alternative summary method for key-value pairs
129    pub fn summary_kv(&self, title: &str, items: &[(&str, String)]) {
130        if !self.quiet {
131            println!("\nšŸ“‹ {}", title);
132            println!("{}", "─".repeat(title.len() + 3));
133            
134            // Find the maximum key length for alignment
135            let max_key_len = items.iter()
136                .map(|(key, _)| key.len())
137                .max()
138                .unwrap_or(0);
139            
140            for (key, value) in items {
141                println!("  {:width$}: {}", key, value, width = max_key_len);
142            }
143            
144            if items.is_empty() {
145                println!("  (No items to display)");
146            }
147        }
148    }
149
150    // Structured list output
151    pub fn list(&self, title: &str, items: &[String]) {
152        if !self.quiet {
153            if !title.is_empty() {
154                println!("\nšŸ“ {}", title);
155            }
156            
157            for (index, item) in items.iter().enumerate() {
158                println!("  {}. {}", index + 1, item);
159            }
160            
161            if items.is_empty() && !title.is_empty() {
162                println!("  (No items in list)");
163            }
164        }
165    }
166
167    // Table-like output for structured data
168    pub fn table(&self, headers: &[&str], rows: &[Vec<String>]) {
169        if !self.quiet && !headers.is_empty() {
170            // Calculate column widths
171            let mut col_widths: Vec<usize> = headers.iter().map(|h| h.len()).collect();
172            
173            for row in rows {
174                for (i, cell) in row.iter().enumerate() {
175                    if i < col_widths.len() {
176                        col_widths[i] = col_widths[i].max(cell.len());
177                    }
178                }
179            }
180            
181            // Print header
182            print!("  ");
183            for (i, header) in headers.iter().enumerate() {
184                print!("{:width$}", header, width = col_widths[i]);
185                if i < headers.len() - 1 {
186                    print!(" │ ");
187                }
188            }
189            println!();
190            
191            // Print separator
192            print!("  ");
193            for (i, &width) in col_widths.iter().enumerate() {
194                print!("{}", "─".repeat(width));
195                if i < col_widths.len() - 1 {
196                    print!("─┼─");
197                }
198            }
199            println!();
200            
201            // Print rows
202            for row in rows {
203                print!("  ");
204                for (i, cell) in row.iter().enumerate() {
205                    if i < col_widths.len() {
206                        print!("{:width$}", cell, width = col_widths[i]);
207                        if i < headers.len() - 1 {
208                            print!(" │ ");
209                        }
210                    }
211                }
212                println!();
213            }
214        }
215    }
216
217    // Enhanced progress with metrics
218    pub fn progress_with_metrics(&self, current: u64, total: u64, operation: &str) {
219        if !self.quiet {
220            let percentage = if total > 0 {
221                (current as f64 / total as f64) * 100.0
222            } else {
223                100.0
224            };
225            
226            println!("šŸ“Š {}: {}/{} ({:.1}%)", 
227                operation,
228                self.format_size(current),
229                self.format_size(total),
230                percentage
231            );
232        }
233    }
234
235    // Progress bar visualization
236    pub fn progress_bar(&self, current: u64, total: u64, operation: &str, width: usize) {
237        if !self.quiet {
238            let percentage = if total > 0 {
239                (current as f64 / total as f64) * 100.0
240            } else {
241                100.0
242            };
243            
244            let filled = (width as f64 * (percentage / 100.0)) as usize;
245            let empty = width - filled;
246            
247            let bar = format!("{}{}",
248                "ā–ˆ".repeat(filled),
249                "ā–‘".repeat(empty)
250            );
251            
252            print!("\ršŸ“Š {}: [{}] {:.1}% ({}/{})",
253                operation,
254                bar,
255                percentage,
256                self.format_size(current),
257                self.format_size(total)
258            );
259            
260            let _ = io::stdout().flush();
261            
262            if current >= total {
263                println!(); // New line when complete
264            }
265        }
266    }
267
268    // Helper methods for formatting
269    pub fn format_size(&self, bytes: u64) -> String {
270        const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
271        let mut size = bytes as f64;
272        let mut unit_index = 0;
273        
274        while size >= 1024.0 && unit_index < UNITS.len() - 1 {
275            size /= 1024.0;
276            unit_index += 1;
277        }
278        
279        if unit_index == 0 {
280            format!("{} {}", bytes, UNITS[unit_index])
281        } else {
282            format!("{:.1} {}", size, UNITS[unit_index])
283        }
284    }
285
286    pub fn format_duration(&self, duration: Duration) -> String {
287        let total_seconds = duration.as_secs();
288        let hours = total_seconds / 3600;
289        let minutes = (total_seconds % 3600) / 60;
290        let seconds = total_seconds % 60;
291        
292        if hours > 0 {
293            format!("{}h {}m {}s", hours, minutes, seconds)
294        } else if minutes > 0 {
295            format!("{}m {}s", minutes, seconds)
296        } else {
297            format!("{}s", seconds)
298        }
299    }
300
301    // Format speed (bytes per second)
302    pub fn format_speed(&self, bytes_per_second: u64) -> String {
303        format!("{}/s", self.format_size(bytes_per_second))
304    }
305
306    // Format percentage
307    pub fn format_percentage(&self, current: u64, total: u64) -> String {
308        if total == 0 {
309            "100.0%".to_string()
310        } else {
311            format!("{:.1}%", (current as f64 / total as f64) * 100.0)
312        }
313    }
314
315    // Get elapsed time since start
316    pub fn elapsed_time(&self) -> Duration {
317        self.start_time
318            .map(|start| start.elapsed())
319            .unwrap_or_else(|| Duration::from_secs(0))
320    }
321
322    // Reset start time
323    pub fn reset_timer(&mut self) {
324        self.start_time = Some(Instant::now());
325    }
326}