Skip to main content

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 completes successfully.
114    ///
115    /// Not called if the operation fails or results in a partial extraction.
116    /// Implementors must not rely on this method for cleanup — use `Drop`
117    /// instead.
118    fn on_complete(&mut self);
119}
120
121/// No-op implementation of `ProgressCallback` that does nothing.
122///
123/// Use this when you don't need progress reporting but the API requires
124/// a callback implementation.
125#[derive(Debug, Default)]
126pub struct NoopProgress;
127
128impl ProgressCallback for NoopProgress {
129    fn on_entry_start(&mut self, _path: &Path, _total: usize, _current: usize) {}
130
131    fn on_bytes_written(&mut self, _bytes: u64) {}
132
133    fn on_entry_complete(&mut self, _path: &Path) {}
134
135    fn on_complete(&mut self) {}
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    #[test]
143    fn test_new_report() {
144        let report = ExtractionReport::new();
145        assert_eq!(report.files_extracted, 0);
146        assert_eq!(report.directories_created, 0);
147        assert_eq!(report.bytes_written, 0);
148        assert!(!report.has_warnings());
149    }
150
151    #[test]
152    fn test_add_warning() {
153        let mut report = ExtractionReport::new();
154        report.add_warning("Test warning".to_string());
155        assert!(report.has_warnings());
156        assert_eq!(report.warnings.len(), 1);
157    }
158
159    #[test]
160    fn test_total_items() {
161        let mut report = ExtractionReport::new();
162        report.files_extracted = 10;
163        report.directories_created = 5;
164        report.symlinks_created = 2;
165        assert_eq!(report.total_items(), 17);
166    }
167}