exarch_core/report.rs
1//! Extraction operation reporting.
2
3use std::path::Path;
4use std::time::Duration;
5
6/// Report of an archive extraction operation.
7///
8/// Contains statistics and metadata about the extraction process.
9#[derive(Debug, Clone, Default)]
10pub struct ExtractionReport {
11 /// Number of files successfully extracted.
12 pub files_extracted: usize,
13
14 /// Number of directories created.
15 pub directories_created: usize,
16
17 /// Number of symlinks created.
18 pub symlinks_created: usize,
19
20 /// Total bytes written to disk.
21 pub bytes_written: u64,
22
23 /// Duration of the extraction operation.
24 pub duration: Duration,
25
26 /// Number of files skipped due to security checks.
27 pub files_skipped: usize,
28
29 /// Warnings generated during extraction.
30 pub warnings: Vec<String>,
31}
32
33impl ExtractionReport {
34 /// Creates a new empty extraction report.
35 #[must_use]
36 pub fn new() -> Self {
37 Self::default()
38 }
39
40 /// Adds a warning message to the report.
41 pub fn add_warning(&mut self, message: String) {
42 self.warnings.push(message);
43 }
44
45 /// Returns total number of items processed.
46 #[must_use]
47 pub fn total_items(&self) -> usize {
48 self.files_extracted + self.directories_created + self.symlinks_created
49 }
50
51 /// Returns whether any warnings were generated.
52 #[must_use]
53 pub fn has_warnings(&self) -> bool {
54 !self.warnings.is_empty()
55 }
56}
57
58/// Callback trait for progress reporting during archive operations.
59///
60/// Implement this trait to receive progress updates during extraction or
61/// creation. The trait requires `Send` to allow use in multi-threaded contexts.
62///
63/// # Examples
64///
65/// ```
66/// use exarch_core::ProgressCallback;
67/// use std::path::Path;
68///
69/// struct SimpleProgress;
70///
71/// impl ProgressCallback for SimpleProgress {
72/// fn on_entry_start(&mut self, path: &Path, total: usize, current: usize) {
73/// println!("Processing {}/{}: {}", current, total, path.display());
74/// }
75///
76/// fn on_bytes_written(&mut self, bytes: u64) {
77/// // Track bytes written
78/// }
79///
80/// fn on_entry_complete(&mut self, path: &Path) {
81/// println!("Completed: {}", path.display());
82/// }
83///
84/// fn on_complete(&mut self) {
85/// println!("Operation complete");
86/// }
87/// }
88/// ```
89pub trait ProgressCallback: Send {
90 /// Called when starting to process an entry.
91 ///
92 /// # Arguments
93 ///
94 /// * `path` - Path of the entry being processed
95 /// * `total` - Total number of entries in the archive
96 /// * `current` - Current entry number (1-indexed)
97 fn on_entry_start(&mut self, path: &Path, total: usize, current: usize);
98
99 /// Called when bytes are written during extraction or read during creation.
100 ///
101 /// # Arguments
102 ///
103 /// * `bytes` - Number of bytes written/read in this update
104 fn on_bytes_written(&mut self, bytes: u64);
105
106 /// Called when an entry has been completely processed.
107 ///
108 /// # Arguments
109 ///
110 /// * `path` - Path of the entry that was completed
111 fn on_entry_complete(&mut self, path: &Path);
112
113 /// Called when the entire operation is complete.
114 fn on_complete(&mut self);
115}
116
117/// No-op implementation of `ProgressCallback` that does nothing.
118///
119/// Use this when you don't need progress reporting but the API requires
120/// a callback implementation.
121#[derive(Debug, Default)]
122pub struct NoopProgress;
123
124impl ProgressCallback for NoopProgress {
125 fn on_entry_start(&mut self, _path: &Path, _total: usize, _current: usize) {}
126
127 fn on_bytes_written(&mut self, _bytes: u64) {}
128
129 fn on_entry_complete(&mut self, _path: &Path) {}
130
131 fn on_complete(&mut self) {}
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn test_new_report() {
140 let report = ExtractionReport::new();
141 assert_eq!(report.files_extracted, 0);
142 assert_eq!(report.directories_created, 0);
143 assert_eq!(report.bytes_written, 0);
144 assert!(!report.has_warnings());
145 }
146
147 #[test]
148 fn test_add_warning() {
149 let mut report = ExtractionReport::new();
150 report.add_warning("Test warning".to_string());
151 assert!(report.has_warnings());
152 assert_eq!(report.warnings.len(), 1);
153 }
154
155 #[test]
156 fn test_total_items() {
157 let mut report = ExtractionReport::new();
158 report.files_extracted = 10;
159 report.directories_created = 5;
160 report.symlinks_created = 2;
161 assert_eq!(report.total_items(), 17);
162 }
163}