Skip to content

Commit

Permalink
new: Return what was packed/unpacked. (#63)
Browse files Browse the repository at this point in the history
* Return values.

* Return values.

* Remove leading dot.
  • Loading branch information
milesj authored Mar 5, 2024
1 parent 3cf24b0 commit 3fffb9d
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 52 deletions.
87 changes: 55 additions & 32 deletions crates/archive/src/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf>;
}

/// An `Archiver` is an abstraction for packing and unpacking archives,
Expand Down Expand Up @@ -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<F, P>(&self, packer: F) -> ArchiveResult<()>
/// path to the destination archive file, which is also returned
/// from this method.
pub fn pack<F, P>(&self, packer: F) -> ArchiveResult<PathBuf>
where
F: FnOnce(&Path) -> ArchiveResult<P>,
P: ArchivePacker,
Expand Down Expand Up @@ -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)?;

Expand All @@ -174,7 +178,7 @@ impl<'owner> Archiver<'owner> {
}
.into());
}
Some(".tar") => {
Some("tar") => {
#[cfg(feature = "tar")]
self.pack(crate::tar::TarPacker::new)?;

Expand All @@ -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)?;

Expand All @@ -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)?;

Expand All @@ -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)?;

Expand All @@ -218,7 +222,7 @@ impl<'owner> Archiver<'owner> {
}
.into());
}
Some(".zip") => {
Some("zip") => {
#[cfg(feature = "zip")]
self.pack(crate::zip::ZipPacker::new)?;

Expand All @@ -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<F, P>(&self, unpacker: F) -> ArchiveResult<()>
pub fn unpack<F, P>(&self, unpacker: F) -> ArchiveResult<PathBuf>
where
F: FnOnce(&Path, &Path) -> ArchiveResult<P>,
P: ArchiveUnpacker,
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -364,6 +387,6 @@ impl<'owner> Archiver<'owner> {
}
};

Ok(())
Ok((ext.unwrap(), out))
}
}
10 changes: 6 additions & 4 deletions crates/archive/src/gz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<GzEncoder<File>>,
file_count: usize,
Expand Down Expand Up @@ -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<PathBuf> {
trace!(output_dir = ?self.output_dir, "Ungzipping file");

let mut bytes = vec![];
Expand All @@ -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)
}
}
24 changes: 12 additions & 12 deletions crates/archive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
let file_name = fs::file_name(path);
Expand All @@ -65,7 +65,7 @@ pub fn get_full_file_extension(path: &Path) -> Option<String> {

// 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
Expand All @@ -77,15 +77,15 @@ pub fn get_supported_archive_extensions() -> Vec<String> {
// 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(),
]
}

Expand Down
4 changes: 2 additions & 2 deletions crates/archive/src/tar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf> {
self.archive.set_overwrite(true);

trace!(output_dir = ?self.output_dir, "Opening tarball");
Expand Down Expand Up @@ -224,6 +224,6 @@ impl ArchiveUnpacker for TarUnpacker {

trace!("Unpacked {} files", count);

Ok(())
Ok(self.output_dir.clone())
}
}
4 changes: 2 additions & 2 deletions crates/archive/src/zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf> {
trace!(output_dir = ?self.output_dir, "Opening zip");

let mut count = 0;
Expand Down Expand Up @@ -185,6 +185,6 @@ impl ArchiveUnpacker for ZipUnpacker {

trace!("Unpacked {} files", count);

Ok(())
Ok(self.output_dir.clone())
}
}

0 comments on commit 3fffb9d

Please sign in to comment.