docker_image_pusher/registry/
progress.rs1use crate::logging::Logger;
4use std::time::{Duration, Instant};
5
6#[derive(Clone)]
7pub struct ProgressTracker {
8 total_size: u64,
9 start_time: Instant,
10 last_update: Instant,
11 last_uploaded: u64,
12 output: Logger,
13 operation_name: String,
14}
15
16impl ProgressTracker {
17 pub fn new(total_size: u64, output: Logger, operation_name: String) -> Self {
18 Self {
19 total_size,
20 start_time: Instant::now(),
21 last_update: Instant::now(),
22 last_uploaded: 0,
23 output,
24 operation_name,
25 }
26 }
27
28 pub fn update(&mut self, uploaded: u64) {
29 let now = Instant::now();
30 let elapsed_since_last = now.duration_since(self.last_update);
31
32 let size_threshold = std::cmp::min(10 * 1024 * 1024, self.total_size / 20); if elapsed_since_last >= Duration::from_secs(5)
36 || uploaded - self.last_uploaded >= size_threshold
37 || uploaded == self.total_size
38 {
39 let percent = (uploaded as f64 / self.total_size as f64 * 100.0) as u8;
40 let speed = if elapsed_since_last.as_secs() > 0 {
41 (uploaded - self.last_uploaded) / elapsed_since_last.as_secs()
42 } else {
43 0
44 };
45 self.output.progress(&format!(
46 "{}: {}% ({}/{}) - {} MB/s",
47 self.operation_name,
48 percent,
49 self.output.format_size(uploaded),
50 self.output.format_size(self.total_size),
51 speed / 1024 / 1024
52 ));
53 self.last_update = now;
54 self.last_uploaded = uploaded;
55 }
56 }
57
58 pub fn finish(&self) {
59 self.output.progress(&format!(
60 "{}: 100% ({}/{})",
61 self.operation_name,
62 self.output.format_size(self.total_size),
63 self.output.format_size(self.total_size)
64 ));
65
66 let total_elapsed = self.start_time.elapsed();
67 let avg_speed = if total_elapsed.as_secs() > 0 {
68 self.total_size / total_elapsed.as_secs()
69 } else {
70 self.total_size
71 };
72
73 self.output.success(&format!(
74 "{} completed in {} (avg speed: {})",
75 self.operation_name,
76 self.output.format_duration(total_elapsed),
77 self.output.format_size(avg_speed)
78 ));
79 }
80
81 pub fn set_phase(&mut self, phase: &str) {
82 self.operation_name = format!("{} - {}", self.operation_name, phase);
83 }
84
85 pub fn get_elapsed_time(&self) -> Duration {
86 self.start_time.elapsed()
87 }
88
89 pub fn get_estimated_remaining(&self, uploaded: u64) -> Option<Duration> {
90 let elapsed = self.start_time.elapsed();
91 if uploaded > 0 && elapsed.as_secs() > 0 {
92 let speed = uploaded / elapsed.as_secs();
93 if speed > 0 {
94 let remaining_bytes = self.total_size.saturating_sub(uploaded);
95 return Some(Duration::from_secs(remaining_bytes / speed));
96 }
97 }
98 None
99 }
100}