exarch_core/creation/
report.rs1use std::time::Duration;
4
5#[derive(Debug, Clone, Default)]
23pub struct CreationReport {
24 pub files_added: usize,
26
27 pub directories_added: usize,
29
30 pub symlinks_added: usize,
32
33 pub bytes_written: u64,
35
36 pub bytes_compressed: u64,
38
39 pub duration: Duration,
41
42 pub files_skipped: usize,
44
45 pub warnings: Vec<String>,
47}
48
49impl CreationReport {
50 #[must_use]
52 pub fn new() -> Self {
53 Self::default()
54 }
55
56 pub fn add_warning(&mut self, msg: impl Into<String>) {
68 self.warnings.push(msg.into());
69 }
70
71 #[must_use]
85 pub fn has_warnings(&self) -> bool {
86 !self.warnings.is_empty()
87 }
88
89 #[must_use]
113 pub fn compression_ratio(&self) -> f64 {
114 if self.bytes_compressed == 0 || self.bytes_written == 0 {
115 return 0.0;
116 }
117 self.bytes_written as f64 / self.bytes_compressed as f64
118 }
119
120 #[must_use]
144 pub fn compression_percentage(&self) -> f64 {
145 if self.bytes_written == 0 {
146 return 0.0;
147 }
148 if self.bytes_compressed == 0 {
149 return 100.0;
150 }
151 let saved = self.bytes_written.saturating_sub(self.bytes_compressed);
152 (saved as f64 / self.bytes_written as f64) * 100.0
153 }
154
155 #[must_use]
169 pub fn total_items(&self) -> usize {
170 self.files_added + self.directories_added + self.symlinks_added
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn test_creation_report_default() {
180 let report = CreationReport::default();
181 assert_eq!(report.files_added, 0);
182 assert_eq!(report.directories_added, 0);
183 assert_eq!(report.symlinks_added, 0);
184 assert_eq!(report.bytes_written, 0);
185 assert_eq!(report.bytes_compressed, 0);
186 assert_eq!(report.duration, Duration::default());
187 assert_eq!(report.files_skipped, 0);
188 assert!(report.warnings.is_empty());
189 assert!(!report.has_warnings());
190 }
191
192 #[test]
193 fn test_creation_report_new() {
194 let report = CreationReport::new();
195 assert_eq!(report.files_added, 0);
196 assert!(!report.has_warnings());
197 }
198
199 #[test]
200 #[allow(clippy::float_cmp)]
201 fn test_creation_report_compression_ratio() {
202 let mut report = CreationReport::new();
203
204 report.bytes_written = 1000;
206 report.bytes_compressed = 500;
207 assert_eq!(report.compression_ratio(), 2.0);
208
209 report.bytes_written = 1000;
211 report.bytes_compressed = 1000;
212 assert_eq!(report.compression_ratio(), 1.0);
213
214 report.bytes_written = 500;
216 report.bytes_compressed = 1000;
217 assert_eq!(report.compression_ratio(), 0.5);
218
219 report.bytes_written = 1000;
221 report.bytes_compressed = 0;
222 assert_eq!(report.compression_ratio(), 0.0);
223
224 report.bytes_written = 0;
226 report.bytes_compressed = 500;
227 assert_eq!(report.compression_ratio(), 0.0);
228
229 report.bytes_written = 0;
231 report.bytes_compressed = 0;
232 assert_eq!(report.compression_ratio(), 0.0);
233 }
234
235 #[test]
236 #[allow(clippy::float_cmp)]
237 fn test_creation_report_compression_percentage() {
238 let mut report = CreationReport::new();
239
240 report.bytes_written = 1000;
242 report.bytes_compressed = 500;
243 assert_eq!(report.compression_percentage(), 50.0);
244
245 report.bytes_written = 1000;
247 report.bytes_compressed = 250;
248 assert_eq!(report.compression_percentage(), 75.0);
249
250 report.bytes_written = 1000;
252 report.bytes_compressed = 1000;
253 assert_eq!(report.compression_percentage(), 0.0);
254
255 report.bytes_written = 500;
257 report.bytes_compressed = 1000;
258 assert_eq!(report.compression_percentage(), 0.0);
259
260 report.bytes_written = 1000;
262 report.bytes_compressed = 0;
263 assert_eq!(report.compression_percentage(), 100.0);
264
265 report.bytes_written = 0;
267 report.bytes_compressed = 500;
268 assert_eq!(report.compression_percentage(), 0.0);
269
270 report.bytes_written = 0;
272 report.bytes_compressed = 0;
273 assert_eq!(report.compression_percentage(), 0.0);
274 }
275
276 #[test]
277 fn test_creation_report_warnings() {
278 let mut report = CreationReport::new();
279 assert!(!report.has_warnings());
280
281 report.add_warning("Warning 1");
282 assert!(report.has_warnings());
283 assert_eq!(report.warnings.len(), 1);
284 assert_eq!(report.warnings[0], "Warning 1");
285
286 report.add_warning("Warning 2".to_string());
287 assert_eq!(report.warnings.len(), 2);
288 assert_eq!(report.warnings[1], "Warning 2");
289
290 let string_ref = String::from("Warning 3");
291 report.add_warning(&string_ref);
292 assert_eq!(report.warnings.len(), 3);
293 }
294
295 #[test]
296 fn test_creation_report_total_items() {
297 let mut report = CreationReport::new();
298 assert_eq!(report.total_items(), 0);
299
300 report.files_added = 10;
301 report.directories_added = 5;
302 report.symlinks_added = 2;
303 assert_eq!(report.total_items(), 17);
304
305 report.files_added = 0;
306 assert_eq!(report.total_items(), 7);
307 }
308
309 #[test]
310 fn test_creation_report_real_scenario() {
311 let mut report = CreationReport::new();
312 report.files_added = 100;
313 report.directories_added = 20;
314 report.symlinks_added = 5;
315 report.bytes_written = 10 * 1024 * 1024; report.bytes_compressed = 3 * 1024 * 1024; report.duration = Duration::from_secs(2);
318 report.files_skipped = 3;
319 report.add_warning("Skipped 3 files due to size limit");
320
321 assert_eq!(report.total_items(), 125);
322 assert!(report.has_warnings());
323 assert_eq!(report.warnings.len(), 1);
324
325 let ratio = report.compression_ratio();
327 assert!((ratio - 3.333).abs() < 0.01);
328
329 let percentage = report.compression_percentage();
331 assert!((percentage - 70.0).abs() < 0.1);
332 }
333}