diff --git a/src/archive/tar.rs b/src/archive/tar.rs index 06182f7..ce32a50 100644 --- a/src/archive/tar.rs +++ b/src/archive/tar.rs @@ -4,6 +4,8 @@ use std::{ env, io::prelude::*, path::{Path, PathBuf}, + sync::mpsc::{self, Receiver}, + thread, }; use fs_err as fs; @@ -48,21 +50,31 @@ pub fn unpack_archive( /// List contents of `archive`, returning a vector of archive entries pub fn list_archive( - archive: tar::Archive, -) -> crate::Result>> { - // NOTE: tar::Archive::entries takes a &mut self - // This makes returning an iterator impossible - // Current workaround is just to leak the archive - let archive = Box::leak(Box::new(archive)); + mut archive: tar::Archive, +) -> impl Iterator> { + struct Files(Receiver>); + impl Iterator for Files { + type Item = crate::Result; - Ok(archive.entries()?.map(|file| { - let file = file?; + fn next(&mut self) -> Option { + self.0.recv().ok() + } + } - let path = file.path()?.into_owned(); - let is_dir = file.header().entry_type().is_dir(); + let (tx, rx) = mpsc::channel(); + thread::spawn(move || { + for file in archive.entries().expect("entries is only used once") { + let file_in_archive = (|| { + let file = file?; + let path = file.path()?.into_owned(); + let is_dir = file.header().entry_type().is_dir(); + Ok(FileInArchive { path, is_dir }) + })(); + tx.send(file_in_archive).unwrap(); + } + }); - Ok(FileInArchive { path, is_dir }) - })) + Files(rx) } /// Compresses the archives given by `input_filenames` into the file given previously to `writer`. diff --git a/src/archive/zip.rs b/src/archive/zip.rs index d93e2bf..484c333 100644 --- a/src/archive/zip.rs +++ b/src/archive/zip.rs @@ -4,6 +4,8 @@ use std::{ env, io::{self, prelude::*}, path::{Path, PathBuf}, + sync::mpsc, + thread, }; use fs_err as fs; @@ -82,22 +84,41 @@ where /// List contents of `archive`, returning a vector of archive entries pub fn list_archive(mut archive: ZipArchive) -> impl Iterator> where - R: Read + Seek + 'static, + R: Read + Seek + Send + 'static, { - (0..archive.len()).filter_map(move |idx| { - let file = match archive.by_index(idx) { - Ok(f) => f, - Err(e) => return Some(Err(e.into())), - }; + struct Files(mpsc::Receiver>); + impl Iterator for Files { + type Item = crate::Result; - let path = match file.enclosed_name() { - Some(path) => path.to_owned(), - None => return None, - }; - let is_dir = file.is_dir(); + fn next(&mut self) -> Option { + self.0.recv().ok() + } + } - Some(Ok(FileInArchive { path, is_dir })) - }) + let (tx, rx) = mpsc::channel(); + thread::spawn(move || { + for idx in 0..archive.len() { + let maybe_file_in_archive = (|| { + let file = match archive.by_index(idx) { + Ok(f) => f, + Err(e) => return Some(Err(e.into())), + }; + + let path = match file.enclosed_name() { + Some(path) => path.to_owned(), + None => return None, + }; + let is_dir = file.is_dir(); + + Some(Ok(FileInArchive { path, is_dir })) + })(); + if let Some(file_in_archive) = maybe_file_in_archive { + tx.send(file_in_archive).unwrap(); + } + } + }); + + Files(rx) } /// Compresses the archives given by `input_filenames` into the file given previously to `writer`. diff --git a/src/commands.rs b/src/commands.rs index 304fca6..4ad154b 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -309,7 +309,6 @@ fn compress_files( Bzip => Box::new(bzip2::write::BzEncoder::new(encoder, Default::default())), Lz4 => Box::new(lzzzz::lz4f::WriteCompressor::new(encoder, Default::default())?), Lzma => Box::new(xz2::write::XzEncoder::new(encoder, 6)), - Snappy => Box::new(snap::write::FrameEncoder::new(encoder)), Zstd => { let zstd_encoder = zstd::stream::write::Encoder::new(encoder, Default::default()); // Safety: @@ -463,7 +462,6 @@ fn decompress_file( Bzip => Box::new(bzip2::read::BzDecoder::new(decoder)), Lz4 => Box::new(lzzzz::lz4f::ReadDecompressor::new(decoder)?), Lzma => Box::new(xz2::read::XzDecoder::new(decoder)), - Snappy => Box::new(snap::read::FrameDecoder::new(decoder)), Zstd => Box::new(zstd::stream::Decoder::new(decoder)?), Tar | Zip => unreachable!(), }; @@ -567,7 +565,6 @@ fn list_archive_contents( archive_path: &Path, formats: Vec, list_options: ListOptions, - question_policy: QuestionPolicy, ) -> crate::Result<()> { let reader = fs::File::open(&archive_path)?; @@ -588,18 +585,21 @@ fn list_archive_contents( // Will be used in decoder chaining let reader = BufReader::with_capacity(BUFFER_CAPACITY, reader); - let mut reader: Box = Box::new(reader); + let mut reader: Box = Box::new(reader); // Grab previous decoder and wrap it inside of a new one - let chain_reader_decoder = |format: &CompressionFormat, decoder: Box| -> crate::Result> { - let decoder: Box = match format { - Gzip => Box::new(flate2::read::GzDecoder::new(decoder)), - Bzip => Box::new(bzip2::read::BzDecoder::new(decoder)), - Lz4 => Box::new(lzzzz::lz4f::ReadDecompressor::new(decoder)?), - Lzma => Box::new(xz2::read::XzDecoder::new(decoder)), - Snappy => Box::new(snap::read::FrameDecoder::new(decoder)), - Zstd => Box::new(zstd::stream::Decoder::new(decoder)?), - Tar | Zip => unreachable!(), + let chain_reader_decoder = + |format: &CompressionFormat, decoder: Box| -> crate::Result> { + let decoder: Box = match format { + Gzip => Box::new(flate2::read::GzDecoder::new(decoder)), + Bzip => Box::new(bzip2::read::BzDecoder::new(decoder)), + Lz4 => Box::new(lzzzz::lz4f::ReadDecompressor::new(decoder)?), + Lzma => Box::new(xz2::read::XzDecoder::new(decoder)), + Snappy => Box::new(snap::read::FrameDecoder::new(decoder)), + Zstd => Box::new(zstd::stream::Decoder::new(decoder)?), + Tar | Zip => unreachable!(), + }; + Ok(decoder) }; Ok(decoder) }; @@ -609,7 +609,7 @@ fn list_archive_contents( } let files: Box>> = match formats[0] { - Tar => Box::new(crate::archive::tar::list_archive(tar::Archive::new(reader))?), + Tar => Box::new(crate::archive::tar::list_archive(tar::Archive::new(reader))), Zip => { eprintln!("{orange}[WARNING]{reset}", orange = *colors::ORANGE, reset = *colors::RESET); eprintln!(