docker_image_pusher/output/
mod.rs1use 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 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 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 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 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 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 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 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 pub fn table(&self, headers: &[&str], rows: &[Vec<String>]) {
169 if !self.quiet && !headers.is_empty() {
170 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!(" ");
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!(" ");
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 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 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 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!(); }
265 }
266 }
267
268 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 pub fn format_speed(&self, bytes_per_second: u64) -> String {
303 format!("{}/s", self.format_size(bytes_per_second))
304 }
305
306 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 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 pub fn reset_timer(&mut self) {
324 self.start_time = Some(Instant::now());
325 }
326}