1use std::path::Path;
4
5use crate::ExtractionReport;
6use crate::NoopProgress;
7use crate::ProgressCallback;
8use crate::Result;
9use crate::SecurityConfig;
10use crate::creation::CreationConfig;
11use crate::creation::CreationReport;
12use crate::formats::detect::ArchiveType;
13use crate::formats::detect::detect_format;
14use crate::inspection::ArchiveManifest;
15use crate::inspection::VerificationReport;
16
17pub fn extract_archive<P: AsRef<Path>, Q: AsRef<Path>>(
50 archive_path: P,
51 output_dir: Q,
52 config: &SecurityConfig,
53) -> Result<ExtractionReport> {
54 let mut noop = NoopProgress;
55 extract_archive_with_progress(archive_path, output_dir, config, &mut noop)
56}
57
58pub fn extract_archive_with_progress<P: AsRef<Path>, Q: AsRef<Path>>(
95 archive_path: P,
96 output_dir: Q,
97 config: &SecurityConfig,
98 _progress: &mut dyn ProgressCallback,
99) -> Result<ExtractionReport> {
100 let archive_path = archive_path.as_ref();
101 let output_dir = output_dir.as_ref();
102
103 let format = detect_format(archive_path)?;
105
106 match format {
108 ArchiveType::Tar => extract_tar(archive_path, output_dir, config),
109 ArchiveType::TarGz => extract_tar_gz(archive_path, output_dir, config),
110 ArchiveType::TarBz2 => extract_tar_bz2(archive_path, output_dir, config),
111 ArchiveType::TarXz => extract_tar_xz(archive_path, output_dir, config),
112 ArchiveType::TarZst => extract_tar_zst(archive_path, output_dir, config),
113 ArchiveType::Zip => extract_zip(archive_path, output_dir, config),
114 }
115}
116
117fn extract_tar(
118 archive_path: &Path,
119 output_dir: &Path,
120 config: &SecurityConfig,
121) -> Result<ExtractionReport> {
122 use crate::formats::TarArchive;
123 use crate::formats::traits::ArchiveFormat;
124 use std::fs::File;
125 use std::io::BufReader;
126
127 let file = File::open(archive_path)?;
128 let reader = BufReader::new(file);
129 let mut archive = TarArchive::new(reader);
130 archive.extract(output_dir, config)
131}
132
133fn extract_tar_gz(
134 archive_path: &Path,
135 output_dir: &Path,
136 config: &SecurityConfig,
137) -> Result<ExtractionReport> {
138 use crate::formats::TarArchive;
139 use crate::formats::traits::ArchiveFormat;
140 use flate2::read::GzDecoder;
141 use std::fs::File;
142 use std::io::BufReader;
143
144 let file = File::open(archive_path)?;
145 let reader = BufReader::new(file);
146 let decoder = GzDecoder::new(reader);
147 let mut archive = TarArchive::new(decoder);
148 archive.extract(output_dir, config)
149}
150
151fn extract_tar_bz2(
152 archive_path: &Path,
153 output_dir: &Path,
154 config: &SecurityConfig,
155) -> Result<ExtractionReport> {
156 use crate::formats::TarArchive;
157 use crate::formats::traits::ArchiveFormat;
158 use bzip2::read::BzDecoder;
159 use std::fs::File;
160 use std::io::BufReader;
161
162 let file = File::open(archive_path)?;
163 let reader = BufReader::new(file);
164 let decoder = BzDecoder::new(reader);
165 let mut archive = TarArchive::new(decoder);
166 archive.extract(output_dir, config)
167}
168
169fn extract_tar_xz(
170 archive_path: &Path,
171 output_dir: &Path,
172 config: &SecurityConfig,
173) -> Result<ExtractionReport> {
174 use crate::formats::TarArchive;
175 use crate::formats::traits::ArchiveFormat;
176 use std::fs::File;
177 use std::io::BufReader;
178 use xz2::read::XzDecoder;
179
180 let file = File::open(archive_path)?;
181 let reader = BufReader::new(file);
182 let decoder = XzDecoder::new(reader);
183 let mut archive = TarArchive::new(decoder);
184 archive.extract(output_dir, config)
185}
186
187fn extract_tar_zst(
188 archive_path: &Path,
189 output_dir: &Path,
190 config: &SecurityConfig,
191) -> Result<ExtractionReport> {
192 use crate::formats::TarArchive;
193 use crate::formats::traits::ArchiveFormat;
194 use std::fs::File;
195 use std::io::BufReader;
196 use zstd::stream::read::Decoder as ZstdDecoder;
197
198 let file = File::open(archive_path)?;
199 let reader = BufReader::new(file);
200 let decoder = ZstdDecoder::new(reader)?;
201 let mut archive = TarArchive::new(decoder);
202 archive.extract(output_dir, config)
203}
204
205fn extract_zip(
206 archive_path: &Path,
207 output_dir: &Path,
208 config: &SecurityConfig,
209) -> Result<ExtractionReport> {
210 use crate::formats::ZipArchive;
211 use crate::formats::traits::ArchiveFormat;
212 use std::fs::File;
213
214 let file = File::open(archive_path)?;
215 let mut archive = ZipArchive::new(file)?;
216 archive.extract(output_dir, config)
217}
218
219pub fn create_archive<P: AsRef<Path>, Q: AsRef<Path>>(
252 output_path: P,
253 sources: &[Q],
254 config: &CreationConfig,
255) -> Result<CreationReport> {
256 let mut noop = NoopProgress;
257 create_archive_with_progress(output_path, sources, config, &mut noop)
258}
259
260pub fn create_archive_with_progress<P: AsRef<Path>, Q: AsRef<Path>>(
301 output_path: P,
302 sources: &[Q],
303 config: &CreationConfig,
304 progress: &mut dyn ProgressCallback,
305) -> Result<CreationReport> {
306 let output = output_path.as_ref();
307
308 let format = determine_creation_format(output, config)?;
310
311 match format {
313 ArchiveType::Tar => {
314 crate::creation::tar::create_tar_with_progress(output, sources, config, progress)
315 }
316 ArchiveType::TarGz => {
317 crate::creation::tar::create_tar_gz_with_progress(output, sources, config, progress)
318 }
319 ArchiveType::TarBz2 => {
320 crate::creation::tar::create_tar_bz2_with_progress(output, sources, config, progress)
321 }
322 ArchiveType::TarXz => {
323 crate::creation::tar::create_tar_xz_with_progress(output, sources, config, progress)
324 }
325 ArchiveType::TarZst => {
326 crate::creation::tar::create_tar_zst_with_progress(output, sources, config, progress)
327 }
328 ArchiveType::Zip => {
329 crate::creation::zip::create_zip_with_progress(output, sources, config, progress)
330 }
331 }
332}
333
334pub fn list_archive<P: AsRef<Path>>(
369 archive_path: P,
370 config: &SecurityConfig,
371) -> Result<ArchiveManifest> {
372 crate::inspection::list_archive(archive_path, config)
373}
374
375pub fn verify_archive<P: AsRef<Path>>(
419 archive_path: P,
420 config: &SecurityConfig,
421) -> Result<VerificationReport> {
422 crate::inspection::verify_archive(archive_path, config)
423}
424
425fn determine_creation_format(output: &Path, config: &CreationConfig) -> Result<ArchiveType> {
427 if let Some(format) = config.format {
429 return Ok(format);
430 }
431
432 detect_format(output)
434}
435
436#[cfg(test)]
437#[allow(clippy::unwrap_used)]
438mod tests {
439 use super::*;
440 use std::path::PathBuf;
441
442 #[test]
443 fn test_extract_archive_nonexistent_file() {
444 let config = SecurityConfig::default();
445 let result = extract_archive(
446 PathBuf::from("nonexistent_test.tar"),
447 PathBuf::from("/tmp/test"),
448 &config,
449 );
450 assert!(result.is_err());
452 }
453
454 #[test]
455 fn test_determine_creation_format_tar() {
456 let config = CreationConfig::default();
457 let path = PathBuf::from("archive.tar");
458 let format = determine_creation_format(&path, &config).unwrap();
459 assert_eq!(format, ArchiveType::Tar);
460 }
461
462 #[test]
463 fn test_determine_creation_format_tar_gz() {
464 let config = CreationConfig::default();
465 let path = PathBuf::from("archive.tar.gz");
466 let format = determine_creation_format(&path, &config).unwrap();
467 assert_eq!(format, ArchiveType::TarGz);
468
469 let path2 = PathBuf::from("archive.tgz");
470 let format2 = determine_creation_format(&path2, &config).unwrap();
471 assert_eq!(format2, ArchiveType::TarGz);
472 }
473
474 #[test]
475 fn test_determine_creation_format_tar_bz2() {
476 let config = CreationConfig::default();
477 let path = PathBuf::from("archive.tar.bz2");
478 let format = determine_creation_format(&path, &config).unwrap();
479 assert_eq!(format, ArchiveType::TarBz2);
480 }
481
482 #[test]
483 fn test_determine_creation_format_tar_xz() {
484 let config = CreationConfig::default();
485 let path = PathBuf::from("archive.tar.xz");
486 let format = determine_creation_format(&path, &config).unwrap();
487 assert_eq!(format, ArchiveType::TarXz);
488 }
489
490 #[test]
491 fn test_determine_creation_format_tar_zst() {
492 let config = CreationConfig::default();
493 let path = PathBuf::from("archive.tar.zst");
494 let format = determine_creation_format(&path, &config).unwrap();
495 assert_eq!(format, ArchiveType::TarZst);
496 }
497
498 #[test]
499 fn test_determine_creation_format_zip() {
500 let config = CreationConfig::default();
501 let path = PathBuf::from("archive.zip");
502 let format = determine_creation_format(&path, &config).unwrap();
503 assert_eq!(format, ArchiveType::Zip);
504 }
505
506 #[test]
507 fn test_determine_creation_format_explicit() {
508 let config = CreationConfig::default().with_format(Some(ArchiveType::TarGz));
509 let path = PathBuf::from("archive.xyz");
510 let format = determine_creation_format(&path, &config).unwrap();
511 assert_eq!(format, ArchiveType::TarGz);
512 }
513
514 #[test]
515 fn test_determine_creation_format_unknown() {
516 let config = CreationConfig::default();
517 let path = PathBuf::from("archive.rar");
518 let result = determine_creation_format(&path, &config);
519 assert!(result.is_err());
520 }
521}