pulith_fetch/progress/
progress.rs1use std::fmt;
2
3use crate::config::fetch_options::FetchPhase;
4
5#[derive(Debug, Clone, PartialEq)]
10pub struct Progress {
11 pub phase: FetchPhase,
13
14 pub bytes_downloaded: u64,
16
17 pub total_bytes: Option<u64>,
22
23 pub retry_count: u32,
25
26 pub performance_metrics: Option<PerformanceMetrics>,
28}
29
30#[derive(Debug, Clone, PartialEq, Default)]
32pub struct PerformanceMetrics {
33 pub current_rate_bps: Option<f64>,
35
36 pub average_rate_bps: Option<f64>,
38
39 pub bandwidth_limit_bps: Option<u64>,
41
42 pub bandwidth_utilization: Option<f64>,
44
45 pub phase_timings: PhaseTimings,
47
48 pub rate_adjustments: u32,
50
51 pub network_latency_ms: Option<u64>,
53
54 pub connection_time_ms: Option<u64>,
56}
57
58#[derive(Debug, Clone, PartialEq, Default)]
60pub struct PhaseTimings {
61 pub connecting_ms: u64,
63
64 pub downloading_ms: u64,
66
67 pub verifying_ms: u64,
69
70 pub committing_ms: u64,
72}
73
74impl PhaseTimings {
75 #[must_use]
77 pub fn total_ms(&self) -> u64 {
78 self.connecting_ms + self.downloading_ms + self.verifying_ms + self.committing_ms
79 }
80}
81
82impl Progress {
83 #[must_use]
87 pub fn percentage(&self) -> Option<f64> {
88 self.total_bytes.map(|total| {
89 if total == 0 {
90 if self.is_completed() { 100.0 } else { 0.0 }
92 } else {
93 (self.bytes_downloaded as f64 / total as f64) * 100.0
94 }
95 })
96 }
97
98 #[must_use]
100 pub fn is_completed(&self) -> bool {
101 self.phase == FetchPhase::Completed
102 }
103
104 #[must_use]
106 pub fn is_retrying(&self) -> bool {
107 self.retry_count > 0
108 }
109}
110
111impl fmt::Display for Progress {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 match self.percentage() {
114 Some(pct) => write!(
115 f,
116 "{}: {:.1}% ({}/{} bytes, retry {})",
117 self.phase,
118 pct,
119 self.bytes_downloaded,
120 self.total_bytes.unwrap_or(0),
121 self.retry_count
122 ),
123 None => write!(
124 f,
125 "{}: {}/{} bytes (retry {})",
126 self.phase,
127 self.bytes_downloaded,
128 self.total_bytes.unwrap_or(0),
129 self.retry_count
130 ),
131 }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_progress_percentage() {
141 let progress = Progress {
142 phase: FetchPhase::Downloading,
143 bytes_downloaded: 50,
144 total_bytes: Some(100),
145 retry_count: 0,
146 performance_metrics: None,
147 };
148 assert_eq!(progress.percentage(), Some(50.0));
149
150 let progress = Progress {
152 phase: FetchPhase::Downloading,
153 bytes_downloaded: 0,
154 total_bytes: Some(0),
155 retry_count: 0,
156 performance_metrics: None,
157 };
158 assert_eq!(progress.percentage(), Some(0.0));
159
160 let progress = Progress {
162 phase: FetchPhase::Completed,
163 bytes_downloaded: 0,
164 total_bytes: Some(0),
165 retry_count: 0,
166 performance_metrics: None,
167 };
168 assert_eq!(progress.percentage(), Some(100.0));
169
170 let progress = Progress {
172 phase: FetchPhase::Downloading,
173 bytes_downloaded: 50,
174 total_bytes: None,
175 retry_count: 0,
176 performance_metrics: None,
177 };
178 assert_eq!(progress.percentage(), None);
179 }
180
181 #[test]
182 fn test_is_completed() {
183 let progress = Progress {
184 phase: FetchPhase::Completed,
185 bytes_downloaded: 100,
186 total_bytes: Some(100),
187 retry_count: 0,
188 performance_metrics: None,
189 };
190 assert!(progress.is_completed());
191
192 let progress = Progress {
193 phase: FetchPhase::Downloading,
194 bytes_downloaded: 100,
195 total_bytes: Some(100),
196 retry_count: 0,
197 performance_metrics: None,
198 };
199 assert!(!progress.is_completed());
200 }
201
202 #[test]
203 fn test_is_retrying() {
204 let progress = Progress {
205 phase: FetchPhase::Downloading,
206 bytes_downloaded: 50,
207 total_bytes: Some(100),
208 retry_count: 1,
209 performance_metrics: None,
210 };
211 assert!(progress.is_retrying());
212
213 let progress = Progress {
214 phase: FetchPhase::Downloading,
215 bytes_downloaded: 50,
216 total_bytes: Some(100),
217 retry_count: 0,
218 performance_metrics: None,
219 };
220 assert!(!progress.is_retrying());
221 }
222
223 #[test]
224 fn test_performance_metrics_default() {
225 let metrics = PerformanceMetrics::default();
226 assert!(metrics.current_rate_bps.is_none());
227 assert!(metrics.average_rate_bps.is_none());
228 assert!(metrics.bandwidth_limit_bps.is_none());
229 assert!(metrics.bandwidth_utilization.is_none());
230 assert_eq!(metrics.rate_adjustments, 0);
231 assert!(metrics.network_latency_ms.is_none());
232 assert!(metrics.connection_time_ms.is_none());
233 }
234}