diff --git a/crates/archive/src/archive.rs b/crates/archive/src/archive.rs index 849a8712..f8d3a313 100644 --- a/crates/archive/src/archive.rs +++ b/crates/archive/src/archive.rs @@ -28,7 +28,7 @@ pub trait ArchivePacker { pub trait ArchiveUnpacker { /// Unpack the archive to the destination directory. If a prefix is provided, /// remove it from the start of all file paths within the archive. - fn unpack(&mut self, prefix: &str, differ: &mut TreeDiffer) -> ArchiveResult<()>; + fn unpack(&mut self, prefix: &str, differ: &mut TreeDiffer) -> ArchiveResult; } /// An `Archiver` is an abstraction for packing and unpacking archives, @@ -110,8 +110,9 @@ impl<'owner> Archiver<'owner> { /// Pack and create the archive with the added source, using the /// provided packer factory. The factory is passed an absolute - /// path to the destination archive file. - pub fn pack(&self, packer: F) -> ArchiveResult<()> + /// path to the destination archive file, which is also returned + /// from this method. + pub fn pack(&self, packer: F) -> ArchiveResult where F: FnOnce(&Path) -> ArchiveResult

, P: ArchivePacker, @@ -156,14 +157,17 @@ impl<'owner> Archiver<'owner> { archive.pack()?; - Ok(()) + Ok(self.archive_file.to_path_buf()) } /// Determine the packer to use based on the archive file extension, /// then pack the archive using [`Archiver.pack`]. - pub fn pack_from_ext(&self) -> ArchiveResult<()> { - match get_full_file_extension(self.archive_file).as_deref() { - Some(".gz") => { + pub fn pack_from_ext(&self) -> ArchiveResult<(String, PathBuf)> { + let ext = get_full_file_extension(self.archive_file); + let out = self.archive_file.to_path_buf(); + + match ext.as_deref() { + Some("gz") => { #[cfg(feature = "gz")] self.pack(crate::gz::GzPacker::new)?; @@ -174,7 +178,7 @@ impl<'owner> Archiver<'owner> { } .into()); } - Some(".tar") => { + Some("tar") => { #[cfg(feature = "tar")] self.pack(crate::tar::TarPacker::new)?; @@ -185,7 +189,7 @@ impl<'owner> Archiver<'owner> { } .into()); } - Some(".tar.gz" | ".tgz") => { + Some("tar.gz" | "tgz") => { #[cfg(feature = "tar-gz")] self.pack(crate::tar::TarPacker::new_gz)?; @@ -196,7 +200,7 @@ impl<'owner> Archiver<'owner> { } .into()); } - Some(".tar.xz" | ".txz") => { + Some("tar.xz" | "txz") => { #[cfg(feature = "tar-xz")] self.pack(crate::tar::TarPacker::new_xz)?; @@ -207,7 +211,7 @@ impl<'owner> Archiver<'owner> { } .into()); } - Some(".zst" | ".zstd") => { + Some("zst" | "zstd") => { #[cfg(feature = "tar-zstd")] self.pack(crate::tar::TarPacker::new_zstd)?; @@ -218,7 +222,7 @@ impl<'owner> Archiver<'owner> { } .into()); } - Some(".zip") => { + Some("zip") => { #[cfg(feature = "zip")] self.pack(crate::zip::ZipPacker::new)?; @@ -244,18 +248,19 @@ impl<'owner> Archiver<'owner> { } }; - Ok(()) + Ok((ext.unwrap(), out)) } /// Unpack the archive to the destination root, using the provided /// unpacker factory. The factory is passed an absolute path - /// to the output directory, and the input archive file. + /// to the output directory, and the input archive file. The unpacked + /// directory or file is returned from this method. /// /// When unpacking, we compare files at the destination to those /// in the archive, and only unpack the files if they differ. /// Furthermore, files at the destination that are not in the /// archive are removed entirely. - pub fn unpack(&self, unpacker: F) -> ArchiveResult<()> + pub fn unpack(&self, unpacker: F) -> ArchiveResult where F: FnOnce(&Path, &Path) -> ArchiveResult

, P: ArchiveUnpacker, @@ -273,19 +278,27 @@ impl<'owner> Archiver<'owner> { let mut differ = TreeDiffer::load(self.source_root, lookup_paths)?; let mut archive = unpacker(self.source_root, self.archive_file)?; - archive.unpack(self.prefix, &mut differ)?; + let out = archive.unpack(self.prefix, &mut differ)?; differ.remove_stale_tracked_files(); - Ok(()) + Ok(out) } /// Determine the unpacker to use based on the archive file extension, /// then unpack the archive using [`Archiver.unpack`]. - pub fn unpack_from_ext(&self) -> ArchiveResult<()> { - match get_full_file_extension(self.archive_file).as_deref() { - Some(".gz") => { + /// + /// Returns an absolute path to the directory or file that was created, + /// and the extension that was extracted from the input archive file. + pub fn unpack_from_ext(&self) -> ArchiveResult<(String, PathBuf)> { + let ext = get_full_file_extension(self.archive_file); + let out; + + match ext.as_deref() { + Some("gz") => { #[cfg(feature = "gz")] - self.unpack(crate::gz::GzUnpacker::new)?; + { + out = self.unpack(crate::gz::GzUnpacker::new)?; + } #[cfg(not(feature = "gz"))] return Err(ArchiveError::FeatureNotEnabled { @@ -294,9 +307,11 @@ impl<'owner> Archiver<'owner> { } .into()); } - Some(".tar") => { + Some("tar") => { #[cfg(feature = "tar")] - self.unpack(crate::tar::TarUnpacker::new)?; + { + out = self.unpack(crate::tar::TarUnpacker::new)?; + } #[cfg(not(feature = "tar"))] return Err(ArchiveError::FeatureNotEnabled { @@ -305,9 +320,11 @@ impl<'owner> Archiver<'owner> { } .into()); } - Some(".tar.gz" | ".tgz") => { + Some("tar.gz" | "tgz") => { #[cfg(feature = "tar-gz")] - self.unpack(crate::tar::TarUnpacker::new_gz)?; + { + out = self.unpack(crate::tar::TarUnpacker::new_gz)?; + } #[cfg(not(feature = "tar-gz"))] return Err(ArchiveError::FeatureNotEnabled { @@ -316,9 +333,11 @@ impl<'owner> Archiver<'owner> { } .into()); } - Some(".tar.xz" | ".txz") => { + Some("tar.xz" | "txz") => { #[cfg(feature = "tar-xz")] - self.unpack(crate::tar::TarUnpacker::new_xz)?; + { + out = self.unpack(crate::tar::TarUnpacker::new_xz)?; + } #[cfg(not(feature = "tar-xz"))] return Err(ArchiveError::FeatureNotEnabled { @@ -327,9 +346,11 @@ impl<'owner> Archiver<'owner> { } .into()); } - Some(".zst" | ".zstd") => { + Some("zst" | "zstd") => { #[cfg(feature = "tar-zstd")] - self.unpack(crate::tar::TarUnpacker::new_zstd)?; + { + out = self.unpack(crate::tar::TarUnpacker::new_zstd)?; + } #[cfg(not(feature = "tar-zstd"))] return Err(ArchiveError::FeatureNotEnabled { @@ -338,9 +359,11 @@ impl<'owner> Archiver<'owner> { } .into()); } - Some(".zip") => { + Some("zip") => { #[cfg(feature = "zip")] - self.unpack(crate::zip::ZipUnpacker::new)?; + { + out = self.unpack(crate::zip::ZipUnpacker::new)?; + } #[cfg(not(feature = "zip"))] return Err(ArchiveError::FeatureNotEnabled { @@ -364,6 +387,6 @@ impl<'owner> Archiver<'owner> { } }; - Ok(()) + Ok((ext.unwrap(), out)) } } diff --git a/crates/archive/src/gz.rs b/crates/archive/src/gz.rs index 5c001eb9..0ff7f4a7 100644 --- a/crates/archive/src/gz.rs +++ b/crates/archive/src/gz.rs @@ -11,7 +11,7 @@ use tracing::trace; pub use crate::gz_error::GzError; -/// Applies gzip to a file. +/// Applies gzip to a single file. pub struct GzPacker { archive: Option>, file_count: usize, @@ -90,7 +90,7 @@ impl GzUnpacker { } impl ArchiveUnpacker for GzUnpacker { - fn unpack(&mut self, _prefix: &str, _differ: &mut TreeDiffer) -> ArchiveResult<()> { + fn unpack(&mut self, _prefix: &str, _differ: &mut TreeDiffer) -> ArchiveResult { trace!(output_dir = ?self.output_dir, "Ungzipping file"); let mut bytes = vec![]; @@ -99,8 +99,10 @@ impl ArchiveUnpacker for GzUnpacker { .read_to_end(&mut bytes) .map_err(|error| GzError::UnpackFailure { error })?; - fs::write_file(self.output_dir.join(&self.file_name), bytes)?; + let out_file = self.output_dir.join(&self.file_name); - Ok(()) + fs::write_file(&out_file, bytes)?; + + Ok(out_file) } } diff --git a/crates/archive/src/lib.rs b/crates/archive/src/lib.rs index 42cfe173..4e9ed8f4 100644 --- a/crates/archive/src/lib.rs +++ b/crates/archive/src/lib.rs @@ -50,8 +50,8 @@ where .join("/") } -/// Extract the full extension from a file path with leading dot, -/// like `.tar.gz`, instead of just `.gz`. If no file extension +/// Extract the full extension from a file path without leading dot, +/// like `tar.gz`, instead of just `gz`. If no file extension /// is found, returns `None`.` pub fn get_full_file_extension(path: &Path) -> Option { let file_name = fs::file_name(path); @@ -65,7 +65,7 @@ pub fn get_full_file_extension(path: &Path) -> Option { // This is to handle "unsupported format" scenarios if let Some(ext) = path.extension().and_then(|ext| ext.to_str()) { - return Some(format!(".{ext}")); + return Some(ext.to_owned()); } None @@ -77,15 +77,15 @@ pub fn get_supported_archive_extensions() -> Vec { // Order is important here! Must be from most // specific to least specific! vec![ - ".tar.gz".into(), - ".tar.xz".into(), - ".tar".into(), - ".tgz".into(), - ".txz".into(), - ".gz".into(), - ".zstd".into(), - ".zst".into(), - ".zip".into(), + "tar.gz".into(), + "tar.xz".into(), + "tar".into(), + "tgz".into(), + "txz".into(), + "zstd".into(), + "zst".into(), + "zip".into(), + "gz".into(), ] } diff --git a/crates/archive/src/tar.rs b/crates/archive/src/tar.rs index 67baf023..6aea19e9 100644 --- a/crates/archive/src/tar.rs +++ b/crates/archive/src/tar.rs @@ -179,7 +179,7 @@ impl TarUnpacker { } impl ArchiveUnpacker for TarUnpacker { - fn unpack(&mut self, prefix: &str, differ: &mut TreeDiffer) -> ArchiveResult<()> { + fn unpack(&mut self, prefix: &str, differ: &mut TreeDiffer) -> ArchiveResult { self.archive.set_overwrite(true); trace!(output_dir = ?self.output_dir, "Opening tarball"); @@ -224,6 +224,6 @@ impl ArchiveUnpacker for TarUnpacker { trace!("Unpacked {} files", count); - Ok(()) + Ok(self.output_dir.clone()) } } diff --git a/crates/archive/src/zip.rs b/crates/archive/src/zip.rs index f2f28aad..2d39084c 100644 --- a/crates/archive/src/zip.rs +++ b/crates/archive/src/zip.rs @@ -138,7 +138,7 @@ impl ZipUnpacker { } impl ArchiveUnpacker for ZipUnpacker { - fn unpack(&mut self, prefix: &str, differ: &mut TreeDiffer) -> ArchiveResult<()> { + fn unpack(&mut self, prefix: &str, differ: &mut TreeDiffer) -> ArchiveResult { trace!(output_dir = ?self.output_dir, "Opening zip"); let mut count = 0; @@ -185,6 +185,6 @@ impl ArchiveUnpacker for ZipUnpacker { trace!("Unpacked {} files", count); - Ok(()) + Ok(self.output_dir.clone()) } }