docker_image_pusher/registry/
stats.rs1use crate::logging::Logger;
4use std::time::{Duration, Instant};
5
6#[derive(Debug, Clone)]
8pub struct UploadStats {
9 pub total_bytes: u64,
10 pub uploaded_bytes: u64,
11 pub total_layers: usize,
12 pub uploaded_layers: usize,
13 pub successful_layers: usize,
14 pub skipped_layers: usize,
15 pub failed_layers: usize,
16 pub start_time: Instant,
17 pub current_layer_start: Option<Instant>,
18}
19
20impl UploadStats {
21 pub fn new(total_bytes: u64, total_layers: usize) -> Self {
22 Self {
23 total_bytes,
24 uploaded_bytes: 0,
25 total_layers,
26 uploaded_layers: 0,
27 successful_layers: 0,
28 skipped_layers: 0,
29 failed_layers: 0,
30 start_time: Instant::now(),
31 current_layer_start: None,
32 }
33 }
34
35 pub fn start(&mut self) {
37 self.start_time = Instant::now();
38 }
39
40 pub fn set_total_layers(&mut self, count: usize) {
42 self.total_layers = count;
43 }
44
45 pub fn start_layer(&mut self) {
47 self.current_layer_start = Some(Instant::now());
48 }
49
50 pub fn begin_layer_upload(&mut self, _digest: &str, _size: u64) {
52 self.current_layer_start = Some(Instant::now());
53 }
54
55 pub fn mark_layer_completed(&mut self, _digest: &str) {
57 self.successful_layers += 1;
58 self.uploaded_layers += 1;
59 self.current_layer_start = None;
60 }
61
62 pub fn mark_layer_skipped(&mut self, _digest: &str) {
64 self.skipped_layers += 1;
65 self.uploaded_layers += 1;
66 }
67
68 pub fn mark_layer_failed(&mut self, _digest: &str, _error: String) {
70 self.failed_layers += 1;
71 }
72
73 pub fn total_duration(&self) -> Option<Duration> {
75 Some(self.start_time.elapsed())
76 }
77
78 pub fn finish_layer(&mut self, layer_bytes: u64) {
79 self.uploaded_bytes += layer_bytes;
80 self.uploaded_layers += 1;
81 self.current_layer_start = None;
82 }
83
84 pub fn get_progress_percentage(&self) -> f64 {
85 if self.total_bytes == 0 {
86 100.0
87 } else {
88 (self.uploaded_bytes as f64 / self.total_bytes as f64) * 100.0
89 }
90 }
91
92 pub fn get_average_speed(&self) -> u64 {
93 let elapsed = self.start_time.elapsed().as_secs();
94 if elapsed > 0 {
95 self.uploaded_bytes / elapsed
96 } else {
97 0
98 }
99 }
100
101 pub fn get_eta(&self) -> Option<Duration> {
102 if self.uploaded_bytes == 0 {
103 return None;
104 }
105
106 let _elapsed = self.start_time.elapsed();
107 let remaining_bytes = self.total_bytes.saturating_sub(self.uploaded_bytes);
108 let speed = self.get_average_speed();
109
110 if speed > 0 {
111 Some(Duration::from_secs(remaining_bytes / speed))
112 } else {
113 None
114 }
115 }
116}
117
118#[derive(Debug, Clone)]
120pub struct LayerUploadStats {
121 pub digest: String,
122 pub size: u64,
123 pub uploaded: u64,
124 pub start_time: Instant,
125 pub status: LayerUploadStatus,
126}
127
128#[derive(Debug, Clone, PartialEq)]
129pub enum LayerUploadStatus {
130 Pending,
131 InProgress,
132 Completed,
133 Skipped,
134 Failed(String),
135}
136
137impl LayerUploadStats {
138 pub fn new(digest: String, size: u64) -> Self {
139 Self {
140 digest,
141 size,
142 uploaded: 0,
143 start_time: Instant::now(),
144 status: LayerUploadStatus::Pending,
145 }
146 }
147
148 pub fn start(&mut self) {
149 self.status = LayerUploadStatus::InProgress;
150 self.start_time = Instant::now();
151 }
152
153 pub fn update_progress(&mut self, uploaded: u64) {
154 self.uploaded = uploaded;
155 }
156
157 pub fn complete(&mut self) {
158 self.status = LayerUploadStatus::Completed;
159 self.uploaded = self.size;
160 }
161
162 pub fn skip(&mut self) {
163 self.status = LayerUploadStatus::Skipped;
164 }
165
166 pub fn fail(&mut self, error: String) {
167 self.status = LayerUploadStatus::Failed(error);
168 }
169
170 pub fn get_progress_percentage(&self) -> f64 {
171 if self.size == 0 {
172 100.0
173 } else {
174 (self.uploaded as f64 / self.size as f64) * 100.0
175 }
176 }
177
178 pub fn get_speed(&self) -> u64 {
179 let elapsed = self.start_time.elapsed().as_secs();
180 if elapsed > 0 && self.uploaded > 0 {
181 self.uploaded / elapsed
182 } else {
183 0
184 }
185 }
186}
187
188pub struct ProgressReporter {
190 output: Logger,
191 stats: UploadStats,
192 layer_stats: Vec<LayerUploadStats>,
193}
194
195impl ProgressReporter {
196 pub fn new(output: Logger, total_bytes: u64, total_layers: usize) -> Self {
197 Self {
198 output,
199 stats: UploadStats::new(total_bytes, total_layers),
200 layer_stats: Vec::new(),
201 }
202 }
203
204 pub fn add_layer(&mut self, digest: String, size: u64) {
205 self.layer_stats.push(LayerUploadStats::new(digest, size));
206 }
207
208 pub fn start_layer(&mut self, digest: &str) {
209 self.stats.start_layer();
210 if let Some(layer_stat) = self.layer_stats.iter_mut().find(|l| l.digest == digest) {
211 layer_stat.start();
212 }
213 }
214
215 pub fn update_layer_progress(&mut self, digest: &str, uploaded: u64) {
216 if let Some(layer_stat) = self.layer_stats.iter_mut().find(|l| l.digest == digest) {
217 layer_stat.update_progress(uploaded);
218 }
219 }
220
221 pub fn finish_layer(&mut self, digest: &str, size: u64) {
222 self.stats.finish_layer(size);
223 if let Some(layer_stat) = self.layer_stats.iter_mut().find(|l| l.digest == digest) {
224 layer_stat.complete();
225 }
226 }
227
228 pub fn skip_layer(&mut self, digest: &str) {
229 if let Some(layer_stat) = self.layer_stats.iter_mut().find(|l| l.digest == digest) {
230 layer_stat.skip();
231 }
232 }
233
234 pub fn fail_layer(&mut self, digest: &str, error: String) {
235 if let Some(layer_stat) = self.layer_stats.iter_mut().find(|l| l.digest == digest) {
236 layer_stat.fail(error);
237 }
238 }
239
240 pub fn report_progress(&self) {
241 let progress = self.stats.get_progress_percentage();
242 let speed = self.stats.get_average_speed();
243
244 self.output.info(&format!(
245 "Upload progress: {:.1}% ({}/{} layers, avg speed: {})",
246 progress,
247 self.stats.uploaded_layers,
248 self.stats.total_layers,
249 self.output.format_speed(speed)
250 ));
251
252 if let Some(eta) = self.stats.get_eta() {
253 self.output.detail(&format!(
254 "Estimated time remaining: {}",
255 self.output.format_duration(eta)
256 ));
257 }
258 }
259
260 pub fn report_final_stats(&self) {
261 let total_time = self.stats.start_time.elapsed();
262 let avg_speed = self.stats.get_average_speed();
263
264 let completed = self
265 .layer_stats
266 .iter()
267 .filter(|l| l.status == LayerUploadStatus::Completed)
268 .count();
269 let skipped = self
270 .layer_stats
271 .iter()
272 .filter(|l| l.status == LayerUploadStatus::Skipped)
273 .count();
274 let failed = self
275 .layer_stats
276 .iter()
277 .filter(|l| matches!(l.status, LayerUploadStatus::Failed(_)))
278 .count();
279
280 self.output.success(&format!(
281 "Upload completed in {} - {} uploaded, {} skipped, {} failed (avg speed: {})",
282 self.output.format_duration(total_time),
283 completed,
284 skipped,
285 failed,
286 self.output.format_speed(avg_speed)
287 ));
288 }
289}