exarch_core/
archive.rs

1//! Archive types and builders.
2
3use std::path::Path;
4use std::path::PathBuf;
5
6use crate::ExtractionReport;
7use crate::Result;
8use crate::SecurityConfig;
9
10/// Represents an archive file with associated metadata.
11#[derive(Debug)]
12pub struct Archive {
13    path: PathBuf,
14    config: SecurityConfig,
15}
16
17impl Archive {
18    /// Creates a new `Archive` from a file path.
19    ///
20    /// # Errors
21    ///
22    /// Returns an error if the file doesn't exist or cannot be accessed.
23    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
24        let path = path.as_ref().to_path_buf();
25        Ok(Self {
26            path,
27            config: SecurityConfig::default(),
28        })
29    }
30
31    /// Returns the path to the archive file.
32    #[must_use]
33    pub fn path(&self) -> &Path {
34        &self.path
35    }
36
37    /// Returns a reference to the security configuration.
38    #[must_use]
39    pub fn config(&self) -> &SecurityConfig {
40        &self.config
41    }
42
43    /// Extracts the archive to the specified directory.
44    ///
45    /// # Errors
46    ///
47    /// Returns an error if extraction fails or security checks are violated.
48    pub fn extract<P: AsRef<Path>>(&self, output_dir: P) -> Result<ExtractionReport> {
49        crate::api::extract_archive(&self.path, output_dir.as_ref(), &self.config)
50    }
51}
52
53/// Builder for configuring archive extraction.
54///
55/// # Examples
56///
57/// ```no_run
58/// use exarch_core::ArchiveBuilder;
59/// use exarch_core::SecurityConfig;
60///
61/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
62/// let report = ArchiveBuilder::new()
63///     .archive("archive.tar.gz")
64///     .output_dir("/tmp/output")
65///     .config(SecurityConfig::permissive())
66///     .extract()?;
67/// # Ok(())
68/// # }
69/// ```
70#[derive(Debug, Default)]
71pub struct ArchiveBuilder {
72    archive_path: Option<PathBuf>,
73    output_dir: Option<PathBuf>,
74    config: Option<SecurityConfig>,
75}
76
77impl ArchiveBuilder {
78    /// Creates a new `ArchiveBuilder`.
79    #[must_use]
80    pub fn new() -> Self {
81        Self::default()
82    }
83
84    /// Sets the archive file path.
85    #[must_use]
86    pub fn archive<P: AsRef<Path>>(mut self, path: P) -> Self {
87        self.archive_path = Some(path.as_ref().to_path_buf());
88        self
89    }
90
91    /// Sets the output directory.
92    #[must_use]
93    pub fn output_dir<P: AsRef<Path>>(mut self, path: P) -> Self {
94        self.output_dir = Some(path.as_ref().to_path_buf());
95        self
96    }
97
98    /// Sets the security configuration.
99    #[must_use]
100    pub fn config(mut self, config: SecurityConfig) -> Self {
101        self.config = Some(config);
102        self
103    }
104
105    /// Executes the extraction with the configured settings.
106    ///
107    /// # Errors
108    ///
109    /// Returns an error if `archive_path` or `output_dir` are not set,
110    /// or if extraction fails.
111    pub fn extract(self) -> Result<ExtractionReport> {
112        let archive_path =
113            self.archive_path
114                .ok_or_else(|| crate::ExtractionError::SecurityViolation {
115                    reason: "archive path not set".to_string(),
116                })?;
117
118        let output_dir =
119            self.output_dir
120                .ok_or_else(|| crate::ExtractionError::SecurityViolation {
121                    reason: "output directory not set".to_string(),
122                })?;
123
124        let config = self.config.unwrap_or_default();
125
126        crate::api::extract_archive(archive_path, output_dir, &config)
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn test_archive_builder() {
136        let builder = ArchiveBuilder::new()
137            .archive("test.tar")
138            .output_dir("/tmp/test");
139
140        assert!(builder.archive_path.is_some());
141        assert!(builder.output_dir.is_some());
142    }
143
144    #[test]
145    fn test_archive_builder_missing_path() {
146        let builder = ArchiveBuilder::new().output_dir("/tmp/test");
147        let result = builder.extract();
148        assert!(result.is_err());
149    }
150
151    #[test]
152    fn test_archive_builder_missing_output() {
153        let builder = ArchiveBuilder::new().archive("test.tar");
154        let result = builder.extract();
155        assert!(result.is_err());
156    }
157}