1use std::io::{self, Write};
7use std::time::{Duration, Instant};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone)]
12pub struct Logger {
13 pub verbose: bool,
14 pub quiet: bool,
15 pub start_time: Option<Instant>,
16}
17
18impl Logger {
19 pub fn new(verbose: bool) -> Self {
20 Self {
21 verbose,
22 quiet: false,
23 start_time: Some(Instant::now()),
24 }
25 }
26
27 pub fn new_quiet() -> Self {
28 Self {
29 verbose: false,
30 quiet: true,
31 start_time: Some(Instant::now()),
32 }
33 }
34
35 pub fn section(&self, title: &str) {
37 if !self.quiet {
38 println!("\n=== {} ===", title);
39 }
40 }
41
42 pub fn subsection(&self, title: &str) {
44 if !self.quiet {
45 println!("\n--- {} ---", title);
46 }
47 }
48
49 pub fn trace(&self, message: &str) {
51 if self.verbose && !self.quiet {
52 println!("🔍 TRACE: {}", message);
53 }
54 }
55
56 pub fn debug(&self, message: &str) {
57 if self.verbose && !self.quiet {
58 println!("🐛 DEBUG: {}", message);
59 }
60 }
61
62 pub fn verbose(&self, message: &str) {
63 if self.verbose && !self.quiet {
64 println!("📝 {}", message);
65 }
66 }
67
68 pub fn info(&self, message: &str) {
70 if !self.quiet {
71 println!("ℹ️ {}", message);
72 }
73 }
74
75 pub fn success(&self, message: &str) {
77 if !self.quiet {
78 println!("✅ {}", message);
79 }
80 }
81
82 pub fn warning(&self, message: &str) {
84 if !self.quiet {
85 println!("⚠️ WARNING: {}", message);
86 }
87 }
88
89 pub fn error(&self, message: &str) {
91 eprintln!("❌ ERROR: {}", message);
92 }
93
94 pub fn step(&self, message: &str) {
96 if !self.quiet {
97 println!("▶️ {}", message);
98 }
99 }
100
101 pub fn progress(&self, message: &str) {
103 if !self.quiet {
104 print!("⏳ {}...", message);
105 let _ = io::stdout().flush();
106 }
107 }
108
109 pub fn progress_done(&self) {
111 if !self.quiet {
112 println!(" Done");
113 }
114 }
115
116 pub fn detail(&self, message: &str) {
118 if self.verbose && !self.quiet {
119 println!(" {}", message);
120 }
121 }
122
123 pub fn summary(&self, title: &str, items: &[String]) {
125 if !self.quiet {
126 println!("\n📋 {}", title);
127 println!("{}", "─".repeat(title.len() + 3));
128
129 for item in items {
130 println!(" • {}", item);
131 }
132
133 if items.is_empty() {
134 println!(" (No items to display)");
135 }
136 }
137 }
138
139 pub fn summary_kv(&self, title: &str, items: &[(&str, String)]) {
141 if !self.quiet {
142 self.subsection(title);
143 for (key, value) in items {
144 println!(" {}: {}", key, value);
145 }
146 }
147 }
148
149 pub fn list(&self, title: &str, items: &[String]) {
151 if !self.quiet {
152 self.subsection(title);
153 for (i, item) in items.iter().enumerate() {
154 println!(" {}. {}", i + 1, item);
155 }
156
157 if items.is_empty() {
158 println!(" (No items to display)");
159 }
160 }
161 }
162
163 pub fn format_size(&self, bytes: u64) -> String {
165 if bytes < 1024 {
166 format!("{} B", bytes)
167 } else if bytes < 1024 * 1024 {
168 format!("{:.1} KB", bytes as f64 / 1024.0)
169 } else if bytes < 1024 * 1024 * 1024 {
170 format!("{:.1} MB", bytes as f64 / (1024.0 * 1024.0))
171 } else {
172 format!("{:.1} GB", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
173 }
174 }
175
176 pub fn format_duration(&self, duration: Duration) -> String {
178 let secs = duration.as_secs();
179 if secs < 60 {
180 format!("{}s", secs)
181 } else if secs < 3600 {
182 format!("{}m{}s", secs / 60, secs % 60)
183 } else {
184 format!("{}h{}m{}s", secs / 3600, (secs % 3600) / 60, secs % 60)
185 }
186 }
187
188 pub fn format_speed(&self, bytes_per_sec: u64) -> String {
190 format!("{}/s", self.format_size(bytes_per_sec))
191 }
192
193 pub fn display_live_progress(&self, progress: &ProgressState) {
195 if self.quiet {
196 return;
197 }
198
199 print!("\r\x1b[K");
201
202 let percentage = progress.get_progress_percentage();
203 let current_speed = progress.get_current_speed();
204
205 let bar_width = 30;
207 let filled = ((percentage / 100.0) * bar_width as f64) as usize;
208 let empty = bar_width - filled;
209 let bar = format!("[{}{}]", "█".repeat(filled), "░".repeat(empty));
210
211 print!("⏳ {} {:.1}% | ", bar, percentage);
213 print!("{}/{} tasks | ", progress.completed_tasks, progress.total_tasks);
214 print!("{} active | ", progress.active_tasks);
215 print!("{} | ", self.format_speed(current_speed));
216
217 if progress.current_concurrent != progress.max_concurrent {
219 print!("🔧 {}/{} concurrent | ", progress.current_concurrent, progress.max_concurrent);
220 } else {
221 print!("{} concurrent | ", progress.current_concurrent);
222 }
223
224 if let Some(eta) = progress.get_estimated_time_remaining() {
226 print!("ETA: {}", self.format_duration(eta));
227 } else {
228 print!("ETA: calculating...");
229 }
230
231 let _ = io::stdout().flush();
232 }
233
234 pub fn display_detailed_progress(&self, progress: &ProgressState) {
236 if self.quiet || !self.verbose {
237 return;
238 }
239
240 self.subsection("Detailed Progress Status");
241
242 println!("📊 Overall Progress:");
244 println!(" • Total Tasks: {}", progress.total_tasks);
245 println!(" • Completed: {} ({:.1}%)", progress.completed_tasks,
246 (progress.completed_tasks as f64 / progress.total_tasks as f64) * 100.0);
247 println!(" • Active: {}", progress.active_tasks);
248 println!(" • Data: {} / {} ({:.1}%)",
249 self.format_size(progress.processed_bytes),
250 self.format_size(progress.total_bytes),
251 progress.get_progress_percentage());
252
253 println!("\n🔧 Concurrency Status:");
255 println!(" • Current: {}/{}", progress.current_concurrent, progress.max_concurrent);
256
257 if !progress.concurrency_adjustments.is_empty() {
259 println!(" • Recent Adjustments:");
260 for adjustment in progress.concurrency_adjustments.iter().rev().take(3) {
261 let elapsed = adjustment.timestamp.elapsed();
262 println!(" - {}s ago: {} → {} ({})",
263 elapsed.as_secs(),
264 adjustment.old_value,
265 adjustment.new_value,
266 adjustment.reason);
267 }
268 }
269
270 if !progress.active_task_details.is_empty() {
272 println!("\n🔄 Active Tasks:");
273 let mut tasks: Vec<_> = progress.active_task_details.values().collect();
274 tasks.sort_by_key(|t| t.layer_index);
275
276 for task in tasks.iter().take(5) { let task_progress = if task.layer_size > 0 {
278 (task.processed_bytes as f64 / task.layer_size as f64) * 100.0
279 } else {
280 0.0
281 };
282 let elapsed = task.start_time.elapsed();
283 let speed = if elapsed.as_secs() > 0 {
284 task.processed_bytes / elapsed.as_secs()
285 } else {
286 0
287 };
288
289 println!(" • Layer {}: {} {:.1}% ({}) - {} - Priority: {}",
290 task.layer_index + 1,
291 task.task_type,
292 task_progress,
293 self.format_size(task.layer_size),
294 self.format_speed(speed),
295 task.priority);
296 }
297
298 if progress.active_task_details.len() > 5 {
299 println!(" • ... and {} more tasks", progress.active_task_details.len() - 5);
300 }
301 }
302
303 println!(); }
305
306 pub fn notify_concurrency_adjustment(&self, old_value: usize, new_value: usize, reason: &str) {
308 if !self.quiet {
309 if new_value > old_value {
310 println!("🔼 Concurrency increased: {} → {} ({})", old_value, new_value, reason);
311 } else if new_value < old_value {
312 println!("🔽 Concurrency decreased: {} → {} ({})", old_value, new_value, reason);
313 }
314 }
315 }
316
317 pub fn notify_task_start(&self, task_type: &str, layer_index: usize, size: u64, priority: u64) {
319 if self.verbose && !self.quiet {
320 println!("🚀 Starting {} task: Layer {} ({}) - Priority: {}",
321 task_type, layer_index + 1, self.format_size(size), priority);
322 }
323 }
324
325 pub fn notify_task_complete(&self, task_type: &str, layer_index: usize, duration: Duration, size: u64) {
327 if self.verbose && !self.quiet {
328 let speed = if duration.as_secs() > 0 {
329 size / duration.as_secs()
330 } else {
331 size
332 };
333 println!("✅ Completed {} task: Layer {} in {} ({})",
334 task_type, layer_index + 1, self.format_duration(duration), self.format_speed(speed));
335 }
336 }
337
338 pub fn display_simple_progress(&self, completed: usize, total: usize, message: &str) {
340 if self.quiet {
341 return;
342 }
343
344 let percentage = if total > 0 {
346 (completed as f64 / total as f64) * 100.0
347 } else {
348 0.0
349 };
350
351 let bar_width = 20;
353 let filled = ((percentage / 100.0) * bar_width as f64) as usize;
354 let empty = bar_width - filled;
355 let bar = format!("[{}{}]", "█".repeat(filled), "░".repeat(empty));
356
357 println!("⏳ {} {:.1}% | {}/{} {} | {}",
359 bar, percentage, completed, total,
360 if total > 1 { "tasks" } else { "task" }, message);
361
362 let _ = io::stdout().flush();
363 }
364
365 }
367
368#[derive(Debug, Clone)]
370pub struct ProgressState {
371 pub total_tasks: usize,
372 pub completed_tasks: usize,
373 pub active_tasks: usize,
374 pub max_concurrent: usize,
375 pub current_concurrent: usize,
376 pub total_bytes: u64,
377 pub processed_bytes: u64,
378 pub start_time: Instant,
379 pub active_task_details: HashMap<String, TaskProgress>,
380 pub concurrency_adjustments: Vec<ConcurrencyAdjustment>,
381}
382
383#[derive(Debug, Clone)]
385pub struct TaskProgress {
386 pub task_id: String,
387 pub task_type: String, pub layer_index: usize,
389 pub layer_size: u64,
390 pub processed_bytes: u64,
391 pub start_time: Instant,
392 pub priority: u64,
393}
394
395#[derive(Debug, Clone)]
397pub struct ConcurrencyAdjustment {
398 pub timestamp: Instant,
399 pub old_value: usize,
400 pub new_value: usize,
401 pub reason: String,
402}
403
404impl ProgressState {
405 pub fn new(total_tasks: usize, max_concurrent: usize, total_bytes: u64) -> Self {
406 Self {
407 total_tasks,
408 completed_tasks: 0,
409 active_tasks: 0,
410 max_concurrent,
411 current_concurrent: max_concurrent,
412 total_bytes,
413 processed_bytes: 0,
414 start_time: Instant::now(),
415 active_task_details: HashMap::new(),
416 concurrency_adjustments: Vec::new(),
417 }
418 }
419
420 pub fn add_task(&mut self, task: TaskProgress) {
421 self.active_task_details.insert(task.task_id.clone(), task);
422 self.active_tasks = self.active_task_details.len();
423 }
424
425 pub fn update_task_progress(&mut self, task_id: &str, processed_bytes: u64) {
426 if let Some(task) = self.active_task_details.get_mut(task_id) {
427 let old_processed = task.processed_bytes;
428 task.processed_bytes = processed_bytes;
429 self.processed_bytes += processed_bytes - old_processed;
430 }
431 }
432
433 pub fn complete_task(&mut self, task_id: &str) {
434 if let Some(task) = self.active_task_details.remove(task_id) {
435 self.completed_tasks += 1;
436 self.active_tasks = self.active_task_details.len();
437 self.processed_bytes += task.layer_size - task.processed_bytes;
439 }
440 }
441
442 pub fn adjust_concurrency(&mut self, new_concurrent: usize, reason: String) {
443 let adjustment = ConcurrencyAdjustment {
444 timestamp: Instant::now(),
445 old_value: self.current_concurrent,
446 new_value: new_concurrent,
447 reason,
448 };
449 self.concurrency_adjustments.push(adjustment);
450 self.current_concurrent = new_concurrent;
451 }
452
453 pub fn get_progress_percentage(&self) -> f64 {
454 if self.total_bytes == 0 {
455 (self.completed_tasks as f64 / self.total_tasks as f64) * 100.0
456 } else {
457 (self.processed_bytes as f64 / self.total_bytes as f64) * 100.0
458 }
459 }
460
461 pub fn get_estimated_time_remaining(&self) -> Option<Duration> {
462 let elapsed = self.start_time.elapsed();
463 if self.processed_bytes == 0 || elapsed.as_secs() == 0 {
464 return None;
465 }
466
467 let rate = self.processed_bytes as f64 / elapsed.as_secs_f64();
468 let remaining_bytes = self.total_bytes - self.processed_bytes;
469 let estimated_seconds = remaining_bytes as f64 / rate;
470
471 Some(Duration::from_secs_f64(estimated_seconds))
472 }
473
474 pub fn get_current_speed(&self) -> u64 {
475 let elapsed = self.start_time.elapsed();
476 if elapsed.as_secs() == 0 {
477 0
478 } else {
479 self.processed_bytes / elapsed.as_secs()
480 }
481 }
482}