From a41b5e3498a162607f7a7b511a976fbe89fcab01 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Sat, 31 Aug 2024 18:02:43 +0800 Subject: [PATCH] fix zip extraction with prefix dirs --- src/core/custom_instructions/buildtools.rs | 26 +- src/core/install.rs | 2 +- src/utils/extraction.rs | 481 +++++++++++---------- src/utils/mod.rs | 2 +- tests/data/zip_with_prefixes.zip | Bin 0 -> 2176 bytes tests/extraction.rs | 121 ++---- 6 files changed, 300 insertions(+), 332 deletions(-) create mode 100644 tests/data/zip_with_prefixes.zip diff --git a/src/core/custom_instructions/buildtools.rs b/src/core/custom_instructions/buildtools.rs index 5908808b..210a3ccd 100644 --- a/src/core/custom_instructions/buildtools.rs +++ b/src/core/custom_instructions/buildtools.rs @@ -9,28 +9,10 @@ pub(super) fn install(path: &Path, config: &InstallConfiguration) -> Result<()> use anyhow::anyhow; fn any_existing_child_path(root: &Path, childs: &[&str]) -> Option { - fn inner_(root: &Path, childs: &[&str]) -> Option { - childs.iter().find_map(|child| { - let child_path = root.join(child); - child_path.exists().then_some(child_path) - }) - } - - if let Some(found) = inner_(root, childs) { - Some(found) - } else { - // Keep looking in sub dir. - // TODO: This is due to the fact that we have poor zip extraction function atm. - // Since it doesn't skip common prefix, we have to manually look for matches - // by getting into sub directories at depth 1. Delete this branch once it can skip prefix. - let Ok(entries) = utils::walk_dir(root, false) else { return None }; - for sub_dir in entries.iter().filter(|p| p.is_dir()) { - if let Some(found) = inner_(sub_dir.as_path(), childs) { - return Some(found); - } - } - None - } + childs.iter().find_map(|child| { + let child_path = root.join(child); + child_path.exists().then_some(child_path) + }) } // VS Build Tools changed their installer binary name to `CamelCase` at some point. diff --git a/src/core/install.rs b/src/core/install.rs index f874b6ca..952c3c94 100644 --- a/src/core/install.rs +++ b/src/core/install.rs @@ -443,7 +443,7 @@ fn try_install_from_path(config: &InstallConfiguration, name: &str, path: &Path) /// If `maybe_file` is a path to compressed file, this will try to extract it to `dest`; /// otherwise this will copy that file into dest. fn extract_or_copy_to(maybe_file: &Path, dest: &Path) -> Result { - if let Ok(extractable) = Extractable::try_from(maybe_file) { + if let Ok(mut extractable) = Extractable::load(maybe_file) { extractable.extract_to(dest)?; Ok(dest.to_path_buf()) } else { diff --git a/src/utils/extraction.rs b/src/utils/extraction.rs index c01c1fd8..1f1ba97d 100644 --- a/src/utils/extraction.rs +++ b/src/utils/extraction.rs @@ -1,293 +1,314 @@ -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use common_path::common_path_all; +use flate2::read::GzDecoder; +use sevenz_rust::{Password, SevenZReader}; +use std::borrow::Borrow; +use std::fs::File; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; -use std::str::FromStr; +use xz2::read::XzDecoder; +use zip::ZipArchive; use crate::utils::progress_bar::Style; use super::progress_bar::ProgressIndicator; -#[derive(Debug, Clone, Copy)] -pub enum ExtractableKind { +enum ExtractableKind { /// `7-zip` compressed files, ended with `.7z` - SevenZ, - Gz, - Xz, - Zip, -} - -impl FromStr for ExtractableKind { - type Err = anyhow::Error; - fn from_str(s: &str) -> std::result::Result { - match s { - "gz" => Ok(Self::Gz), - "xz" => Ok(Self::Xz), - "zip" => Ok(Self::Zip), - "7z" => Ok(Self::SevenZ), - _ => Err(anyhow!("'{s}' is not a supported extrable file format")), - } - } + SevenZ(SevenZReader), + Gz(tar::Archive>), + Xz(tar::Archive>), + Zip(ZipArchive), } -#[derive(Debug, Clone, Copy)] pub struct Extractable<'a> { path: &'a Path, kind: ExtractableKind, } -impl<'a> TryFrom<&'a Path> for Extractable<'a> { - type Error = anyhow::Error; - fn try_from(value: &'a Path) -> std::result::Result { - let ext = value +impl<'a> Extractable<'a> { + pub fn load(path: &'a Path) -> Result { + println!("loading archive '{}'", path.display()); + + let ext = path .extension() - .ok_or_else(|| anyhow!("path '{}' is not extractable because it appears to have no file extension", value.display()))? + .ok_or_else(|| { + anyhow!( + "'{}' is not extractable because it appears to have no file extension", + path.display() + ) + })? .to_str() - .ok_or_else(|| anyhow!("path '{}' is not extractable because it's path contains invalid unicode characters", value.display()))?; + .ok_or_else(|| { + anyhow!( + "'{}' is not extractable because its extension contains invalid unicode characters", + path.display() + ) + })?; - let kind: ExtractableKind = ext.parse()?; - Ok(Self { path: value, kind }) + let kind = match ext { + "7z" => ExtractableKind::SevenZ(SevenZReader::open(path, Password::empty())?), + "zip" => ExtractableKind::Zip(ZipArchive::new(File::open(path)?)?), + "gz" => { + let tar_gz = GzDecoder::new(File::open(path)?); + ExtractableKind::Gz(tar::Archive::new(tar_gz)) + } + "xz" => { + let tar_xz = XzDecoder::new(File::open(path)?); + ExtractableKind::Xz(tar::Archive::new(tar_xz)) + } + _ => bail!("'{ext}' is not a supported extractable file format"), + }; + + Ok(Self { path, kind }) } -} -impl Extractable<'_> { /// Extract current file into a specific directory. /// /// This will extract file under the `root`, make sure it's an empty folder before using this function. - pub fn extract_to(&self, root: &Path) -> Result<()> { + pub fn extract_to(&mut self, root: &Path) -> Result<()> { let indicator = ProgressIndicator::new(); - match self.kind { - ExtractableKind::Zip => extract_zip(self.path, root, indicator), - ExtractableKind::SevenZ => extract_7z(self.path, root, indicator), - ExtractableKind::Gz => { - use flate2::read::GzDecoder; - - let tar_file = std::fs::File::open(self.path)?; - let tar_gz = GzDecoder::new(tar_file); - let mut archive = tar::Archive::new(tar_gz); - extract_tar(&mut archive, self.path, root, indicator) - } - ExtractableKind::Xz => { - use xz2::read::XzDecoder; + let helper = ExtractHelper { + file_path: self.path, + output_dir: root, + indicator, + }; - let tar_file = std::fs::File::open(self.path)?; - let tar_gz = XzDecoder::new(tar_file); - let mut archive = tar::Archive::new(tar_gz); - extract_tar(&mut archive, self.path, root, indicator) - } + match &mut self.kind { + ExtractableKind::Zip(archive) => helper.extract_zip(archive), + ExtractableKind::SevenZ(archive) => helper.extract_7z(archive), + ExtractableKind::Gz(archive) => helper.extract_tar(archive), + ExtractableKind::Xz(archive) => helper.extract_tar(archive), } } } -fn extract_zip(path: &Path, root: &Path, indicator: ProgressIndicator) -> Result<()> { - use zip::ZipArchive; - - println!("loading '{}'", path.display()); - // FIXME: this is too slow for large files, see if it can be optimized. - let file = std::fs::File::open(path)?; - let mut zip_archive = ZipArchive::new(file)?; - let zip_len = zip_archive.len(); +#[derive(Debug, Clone, Copy)] +struct ExtractHelper<'a, T: Sized> { + file_path: &'a Path, + output_dir: &'a Path, + indicator: ProgressIndicator, +} - // Init progress - let bar = (indicator.start)( - zip_len.try_into()?, - format!("extracting file '{}'", path.display()), - Style::Len, - )?; +impl<'a, T: Sized> ExtractHelper<'a, T> { + fn start_progress_bar(&self, len: u64, style: Style) -> Result { + (self.indicator.start)( + len, + format!("extracting file '{}'", self.file_path.display()), + style, + ) + } - for i in 0..zip_len { - let mut zip_file = zip_archive.by_index(i)?; + fn update_progress_bar(&self, bar: &T, prog: u64) { + (self.indicator.update)(bar, prog); + } - let out_path = match zip_file.enclosed_name() { - Some(path) => root.join(path), - None => continue, - }; + fn end_progress_bar(&self, bar: &T) { + (self.indicator.stop)(bar, "extraction complete.".into()); + } - if zip_file.is_dir() { - super::ensure_dir(&out_path)?; - } else { - super::ensure_parent_dir(&out_path)?; - let mut out_file = std::fs::File::create(&out_path)?; - std::io::copy(&mut zip_file, &mut out_file)?; + fn count_prefixes_for_tar(entries: &[tar::Entry]) -> Result { + if entries.len() < 2 { + return Ok(0); } - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - if let Some(mode) = zip_file.unix_mode() { - std::fs::set_permissions(&out_path, std::fs::Permissions::from_mode(mode))?; + let mut all_files = vec![]; + for entry in entries { + if entry.header().entry_type().is_file() { + all_files.push(entry.path()?.to_path_buf()); } } - - (indicator.update)(&bar, i.try_into()?); + let common_prefix = common_path_all(all_files.iter().map(|c| c.borrow())); + Ok(common_prefix + .map(|p| p.components().count()) + .unwrap_or_default()) } - (indicator.stop)(&bar, "extraction complete.".into()); - Ok(()) -} - -fn extract_7z(path: &Path, root: &Path, indicator: ProgressIndicator) -> Result<()> { - use sevenz_rust::{Password, SevenZReader}; + fn count_prefixes_for_zip(entries: &Vec<&str>) -> Result { + if entries.len() < 2 { + return Ok(0); + } - // Open the given 7z file, there shouldn't be any password protected files tho, - // if there is, just let it fail for now. - let mut sz_reader = SevenZReader::open(path, Password::empty()) - .with_context(|| format!("failed to read 7z archive '{}'", path.display()))?; + let all_files = entries + .iter() + .filter_map(|p| (!p.ends_with('/')).then_some(Path::new(p))); + let common_prefix = common_path_all(all_files); + Ok(common_prefix + .map(|p| p.components().count()) + .unwrap_or_default()) + } - // Find common prefix so we can skip them and reserve the only "important" parts. - let entries = &sz_reader.archive().files; - let common_prefix = { + fn count_prefixes_for_7z(entries: &[sevenz_rust::SevenZArchiveEntry]) -> Result { if entries.len() < 2 { - None - } else { - let all_files = entries - .iter() - .filter(|et| !et.is_directory()) - .map(|et| Path::new(et.name())); - common_path_all(all_files) + return Ok(0); } - }; - - let sz_len: u64 = sz_reader - .archive() - .files - .iter() - .filter_map(|e| e.has_stream().then_some(e.size())) - .sum(); - let mut extracted_len: u64 = 0; - - // Init progress bar - let bar = (indicator.start)( - sz_len, - format!("extracting file '{}'", path.display()), - Style::Bytes, - )?; - - sz_reader.for_each_entries(|entry, reader| { - let mut buf = [0_u8; 1024]; - let mut entry_path = PathBuf::from(entry.name()); - if let Some(prefix) = &common_prefix { - let Ok(stripped) = entry_path.strip_prefix(prefix).map(|p| p.to_path_buf()) else { - // meaning this entry is an prefix directory that we don't need - return Ok(true); + + // NB: `.archive().files` is a fxxking lie, it contains directories as well, + // so we still need to filter directories. + let all_files = entries + .iter() + .filter_map(|p| (!p.is_directory()).then_some(Path::new(p.name()))); + let common_prefix = common_path_all(all_files); + Ok(common_prefix + .map(|p| p.components().count()) + .unwrap_or_default()) + } + + fn extract_zip(&self, archive: &mut ZipArchive) -> Result<()> { + let zip_len = archive.len(); + let prefix_count = Self::count_prefixes_for_zip(&archive.file_names().collect())?; + + // Init progress + let bar = self.start_progress_bar(zip_len.try_into()?, Style::Len)?; + + for i in 0..zip_len { + let mut zip_file = archive.by_index(i)?; + let out_path = match zip_file.enclosed_name() { + Some(path) => { + let skipped = path.components().skip(prefix_count).collect::(); + if skipped == PathBuf::new() { + continue; + } + self.output_dir.join(skipped) + } + None => continue, }; - entry_path = stripped; + + if zip_file.is_dir() { + super::ensure_dir(&out_path)?; + } else { + super::ensure_parent_dir(&out_path)?; + let mut out_file = std::fs::File::create(&out_path)?; + std::io::copy(&mut zip_file, &mut out_file)?; + } + + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + if let Some(mode) = zip_file.unix_mode() { + std::fs::set_permissions(&out_path, std::fs::Permissions::from_mode(mode))?; + } + } + + self.update_progress_bar(&bar, i.try_into()?); } + self.end_progress_bar(&bar); - let out_path = root.join(&entry_path); + Ok(()) + } - if entry.is_directory() { - super::ensure_dir(&out_path).map_err(|_| { - sevenz_rust::Error::other(format!( - "unable to create entry directory '{}'", - out_path.display() - )) - })?; - Ok(true) - } else { - super::ensure_parent_dir(&out_path).map_err(|_| { - sevenz_rust::Error::other(format!( - "unable to create parent directory for '{}'", - out_path.display() - )) - })?; + fn extract_7z(&self, archive: &mut SevenZReader) -> Result<()> { + let entries = &archive.archive().files; + let prefix_count = Self::count_prefixes_for_7z(entries)?; + let sz_len: u64 = entries + .iter() + .filter_map(|e| e.has_stream().then_some(e.size())) + .sum(); + let mut extracted_len: u64 = 0; + + // Init progress bar + let bar = self.start_progress_bar(sz_len, Style::Bytes)?; + + archive.for_each_entries(|entry, reader| { + let mut buf = [0_u8; 1024]; + let mut entry_path = PathBuf::from(entry.name()); + entry_path = entry_path.components().skip(prefix_count).collect(); - let mut out_file = std::fs::File::create(&out_path)?; - loop { - let read_size = reader.read(&mut buf)?; - if read_size == 0 { - break Ok(true); + if entry_path == PathBuf::new() { + return Ok(true); + } + let out_path = self.output_dir.join(&entry_path); + + if entry.is_directory() { + super::ensure_dir(&out_path).map_err(|_| { + sevenz_rust::Error::other(format!( + "unable to create entry directory '{}'", + out_path.display() + )) + })?; + Ok(true) + } else { + super::ensure_parent_dir(&out_path).map_err(|_| { + sevenz_rust::Error::other(format!( + "unable to create parent directory for '{}'", + out_path.display() + )) + })?; + + let mut out_file = std::fs::File::create(&out_path)?; + loop { + let read_size = reader.read(&mut buf)?; + if read_size == 0 { + break Ok(true); + } + out_file.write_all(&buf[..read_size])?; + extracted_len += read_size as u64; + // Update progress bar + self.update_progress_bar(&bar, extracted_len); } - out_file.write_all(&buf[..read_size])?; - extracted_len += read_size as u64; - // Update progress bar - (indicator.update)(&bar, extracted_len); } - } - // NB: sevenz-rust does not support `unix-mode` like `zip` does, so we might ended up - // mess up the extracted file's permission... let's hope that never happens. - })?; + // NB: sevenz-rust does not support `unix-mode` like `zip` does, so we might ended up + // mess up the extracted file's permission... let's hope that never happens. + })?; - // Stop progress bar's progress - (indicator.stop)(&bar, "extraction complete.".into()); + self.end_progress_bar(&bar); + Ok(()) + } - Ok(()) -} + fn extract_tar(&self, archive: &mut tar::Archive) -> Result<()> { + #[cfg(unix)] + archive.set_preserve_permissions(true); -fn extract_tar( - archive: &mut tar::Archive, - path: &Path, - root: &Path, - indicator: ProgressIndicator, -) -> Result<()> { - #[cfg(unix)] - archive.set_preserve_permissions(true); - - let entries = archive.entries()?.collect::>(); - // Find common prefix so we can skip them and reserve the only "important" parts. - // Fxxk this, wtf are these types!!!!! - let common_prefix = { - if entries.len() < 2 { - None - } else { - let all_paths = entries - .iter() - .filter_map(|entry| entry.as_ref().ok()) - // Only get the files entry - .filter(|entry| entry.header().entry_type().is_file()) - .filter_map(|f| f.path().map(|p| p.to_path_buf()).ok()) - .collect::>(); - common_path_all(all_paths.iter().map(|pb| pb.as_path())) + let mut entries = vec![]; + for maybe_entry in archive.entries()? { + let entry = maybe_entry?; + entries.push(entry); } - }; - - let total_len: u64 = entries.len().try_into()?; - // Init progress bar - let bar = (indicator.start)( - total_len, - format!("extracting file '{}'", path.display()), - Style::Len, - )?; - - for (idx, maybe_entry) in entries.into_iter().enumerate() { - let mut entry = maybe_entry?; - let entry_path = if let Some(prefix) = &common_prefix { - let Ok(stripped) = entry.path()?.strip_prefix(prefix).map(|p| p.to_path_buf()) else { - // meaning this entry is an prefix directory that we don't need + + let prefix_count = Self::count_prefixes_for_tar(&entries)?; + let total_len: u64 = entries.len().try_into()?; + // Init progress bar + let bar = self.start_progress_bar(total_len, Style::Len)?; + + for (idx, mut entry) in entries.into_iter().enumerate() { + let entry_path = entry.path()?; + let skipped = entry_path + .components() + .skip(prefix_count) + .collect::(); + if skipped == PathBuf::new() { continue; - }; - stripped - } else { - entry.path()?.to_path_buf() - }; - let out_path = root.join(&entry_path); + } - if entry.header().entry_type().is_dir() { - super::ensure_dir(&out_path).with_context(|| { - format!( - "failed to create directory when extracting '{}'", - path.display() - ) - })?; - } else { - super::ensure_parent_dir(&out_path).with_context(|| { - format!( - "failed to create directory when extracting '{}'", - path.display() - ) - })?; + let out_path = self.output_dir.join(&skipped); + + if entry.header().entry_type().is_dir() { + super::ensure_dir(&out_path).with_context(|| { + format!( + "failed to create directory when extracting '{}'", + self.file_path.display() + ) + })?; + } else { + super::ensure_parent_dir(&out_path).with_context(|| { + format!( + "failed to create directory when extracting '{}'", + self.file_path.display() + ) + })?; + + let mut out_file = std::fs::File::create(&out_path)?; + std::io::copy(&mut entry, &mut out_file)?; + } - let mut out_file = std::fs::File::create(&out_path)?; - std::io::copy(&mut entry, &mut out_file)?; + // Update progress bar + self.update_progress_bar(&bar, u64::try_from(idx)? + 1); } - // Update progress bar - (indicator.update)(&bar, u64::try_from(idx)? + 1); - } - - // Stop progress bar's progress - (indicator.stop)(&bar, "extraction complete.".into()); + // Stop progress bar's progress + self.end_progress_bar(&bar); - Ok(()) + Ok(()) + } } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 1112de67..8e366757 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -12,7 +12,7 @@ mod progress_bar; use std::path::{Path, PathBuf}; pub use download::download; -pub use extraction::{Extractable, ExtractableKind}; +pub use extraction::Extractable; pub use file_system::*; pub use process::*; pub use progress_bar::MultiThreadProgress; diff --git a/tests/data/zip_with_prefixes.zip b/tests/data/zip_with_prefixes.zip new file mode 100644 index 0000000000000000000000000000000000000000..8808788ef6d6ea1bc96f906e9a39c05ff198586f GIT binary patch literal 2176 zcmWIWW@Zs#0D-)G;Yct8N=O3fs?37;^30Nq_=2L;w9JasVtoihKLDpT4Sd=X^YTkF zQj4I*A(`?qkrBmmeG*JbOia`(sVG4>i4wz-l9EsjOGFD6Z4z9TW{Bj)?`U2$BF!KS zUs7lubv0(2ihO>qXP|*VAONB8Wp3QYfU-9PfQ&H*Vw`0JMrKEtiYm@!-OVl?|ky7YOIFFfg12n!vyS0EdbW1poj5 literal 0 HcmV?d00001 diff --git a/tests/extraction.rs b/tests/extraction.rs index b6aaf56b..a1d232da 100644 --- a/tests/extraction.rs +++ b/tests/extraction.rs @@ -1,5 +1,5 @@ use custom_rust::utils::{self, Extractable}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use tempfile::TempDir; fn extract_to_temp(filename: &str) -> TempDir { @@ -18,7 +18,7 @@ fn extract_to_temp(filename: &str) -> TempDir { .tempdir_in(&cache_dir) .unwrap(); - let extractable = Extractable::try_from(path.as_path()).unwrap(); + let mut extractable = Extractable::load(path.as_path()).unwrap(); extractable .extract_to(temp_dir.path()) .expect("failed to extract"); @@ -26,6 +26,34 @@ fn extract_to_temp(filename: &str) -> TempDir { temp_dir } +fn assert_normal_archive(extracted: &Path) { + assert!(extracted.join("aaa.txt").is_file()); + assert!(extracted.join("bbb.txt").is_file()); + assert!(extracted.join("f1").is_dir()); + assert!(extracted.join("f1").join("aaa.txt").is_file()); + assert!(extracted.join("f1").join("bbb.txt").is_file()); + assert!(extracted.join("f2").is_dir()); + assert!(extracted.join("f2").join("aaa.txt").is_file()); + assert!(extracted.join("f3").is_dir()); + assert!(extracted.join("f3").join("aaa.txt").is_file()); + assert!(extracted.join("f3").join("bbb.md").is_file()); + assert!(extracted.join("f3").join("ccc").is_file()); +} + +fn assert_extracted_with_prefixes(extracted: &Path) { + assert!(extracted.join("aaa.txt").is_file()); + assert!(extracted.join("bbb.txt").is_file()); + assert!(extracted.join("f1").is_dir()); + assert!(extracted.join("f1").join("aaa.txt").is_file()); + assert!(extracted.join("f1").join("bbb.txt").is_file()); + assert!(extracted.join("f2").is_dir()); + assert!(extracted.join("f2").join("aaa.txt").is_file()); + assert!(extracted.join("f3").is_dir()); + assert!(extracted.join("f3").join("aaa.txt").is_file()); + assert!(extracted.join("f3").join("bbb.md").is_file()); + assert!(extracted.join("f3").join("ccc").is_file()); +} + #[test] fn extracting_simple_zip() { let extracted_dir = extract_to_temp("simple_zip.zip"); @@ -39,18 +67,7 @@ fn extracting_simple_zip() { #[test] fn extracting_normal_zip() { let temp_dir = extract_to_temp("zip_with_sub_folders.zip"); - - assert!(temp_dir.path().join("aaa.txt").is_file()); - assert!(temp_dir.path().join("bbb.txt").is_file()); - assert!(temp_dir.path().join("f1").is_dir()); - assert!(temp_dir.path().join("f1").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f1").join("bbb.txt").is_file()); - assert!(temp_dir.path().join("f2").is_dir()); - assert!(temp_dir.path().join("f2").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f3").is_dir()); - assert!(temp_dir.path().join("f3").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f3").join("bbb.md").is_file()); - assert!(temp_dir.path().join("f3").join("ccc").is_file()); + assert_normal_archive(temp_dir.path()); } #[test] @@ -65,18 +82,7 @@ fn extracting_simple_7z() { #[test] fn extracting_normal_7z() { let temp_dir = extract_to_temp("7z_with_sub_folders.7z"); - - assert!(temp_dir.path().join("aaa.txt").is_file()); - assert!(temp_dir.path().join("bbb.txt").is_file()); - assert!(temp_dir.path().join("f1").is_dir()); - assert!(temp_dir.path().join("f1").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f1").join("bbb.txt").is_file()); - assert!(temp_dir.path().join("f2").is_dir()); - assert!(temp_dir.path().join("f2").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f3").is_dir()); - assert!(temp_dir.path().join("f3").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f3").join("bbb.md").is_file()); - assert!(temp_dir.path().join("f3").join("ccc").is_file()); + assert_normal_archive(temp_dir.path()); } #[test] @@ -92,24 +98,14 @@ fn extracting_simple_gz() { fn extracting_single_file_gz() { let temp_dir = extract_to_temp("single_file.tar.gz"); + println!("content: {:#?}", utils::walk_dir(temp_dir.path(), true)); assert!(temp_dir.path().join("aaa.txt").is_file()); } #[test] fn extracting_normal_gz() { let temp_dir = extract_to_temp("gz_with_sub_folders.tar.gz"); - - assert!(temp_dir.path().join("aaa.txt").is_file()); - assert!(temp_dir.path().join("bbb.txt").is_file()); - assert!(temp_dir.path().join("f1").is_dir()); - assert!(temp_dir.path().join("f1").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f1").join("bbb.txt").is_file()); - assert!(temp_dir.path().join("f2").is_dir()); - assert!(temp_dir.path().join("f2").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f3").is_dir()); - assert!(temp_dir.path().join("f3").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f3").join("bbb.md").is_file()); - assert!(temp_dir.path().join("f3").join("ccc").is_file()); + assert_normal_archive(temp_dir.path()); } #[test] @@ -124,56 +120,25 @@ fn extracting_simple_xz() { #[test] fn extracting_normal_xz() { let temp_dir = extract_to_temp("xz_with_sub_folders.tar.xz"); - - assert!(temp_dir.path().join("aaa.txt").is_file()); - assert!(temp_dir.path().join("bbb.txt").is_file()); - assert!(temp_dir.path().join("f1").is_dir()); - assert!(temp_dir.path().join("f1").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f1").join("bbb.txt").is_file()); - assert!(temp_dir.path().join("f2").is_dir()); - assert!(temp_dir.path().join("f2").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f3").is_dir()); - assert!(temp_dir.path().join("f3").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f3").join("bbb.md").is_file()); - assert!(temp_dir.path().join("f3").join("ccc").is_file()); + assert_normal_archive(temp_dir.path()); } #[test] fn extracting_xz_with_prefix() { let temp_dir = extract_to_temp("xz_with_prefixes.tar.xz"); - - let dir_contents = utils::walk_dir(temp_dir.path(), true).unwrap(); - println!("contents: {:#?}", dir_contents); - - assert!(temp_dir.path().join("aaa.txt").is_file()); - assert!(temp_dir.path().join("bbb.txt").is_file()); - assert!(temp_dir.path().join("f1").is_dir()); - assert!(temp_dir.path().join("f1").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f1").join("bbb.txt").is_file()); - assert!(temp_dir.path().join("f2").is_dir()); - assert!(temp_dir.path().join("f2").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f3").is_dir()); - assert!(temp_dir.path().join("f3").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f3").join("bbb.md").is_file()); - assert!(temp_dir.path().join("f3").join("ccc").is_file()); + assert_extracted_with_prefixes(temp_dir.path()); } #[test] fn extracting_7z_with_prefix() { let temp_dir = extract_to_temp("7z_with_prefixes.7z"); - let dir_contents = utils::walk_dir(temp_dir.path(), true).unwrap(); - println!("contents: {:#?}", dir_contents); + println!("content: {:#?}", utils::walk_dir(temp_dir.path(), true)); + assert_extracted_with_prefixes(temp_dir.path()); +} - assert!(temp_dir.path().join("aaa.txt").is_file()); - assert!(temp_dir.path().join("bbb.txt").is_file()); - assert!(temp_dir.path().join("f1").is_dir()); - assert!(temp_dir.path().join("f1").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f1").join("bbb.txt").is_file()); - assert!(temp_dir.path().join("f2").is_dir()); - assert!(temp_dir.path().join("f2").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f3").is_dir()); - assert!(temp_dir.path().join("f3").join("aaa.txt").is_file()); - assert!(temp_dir.path().join("f3").join("bbb.md").is_file()); - assert!(temp_dir.path().join("f3").join("ccc").is_file()); +#[test] +fn extracting_zip_with_prefix() { + let temp_dir = extract_to_temp("zip_with_prefixes.zip"); + assert_extracted_with_prefixes(temp_dir.path()); }