Skip to content

Commit

Permalink
Merge pull request #225 from sigmaSd/list
Browse files Browse the repository at this point in the history
List command: print file immediatly after it is processed
  • Loading branch information
marcospb19 committed Dec 20, 2021
2 parents 7a358ef + 3ce9927 commit 308b8f7
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 47 deletions.
38 changes: 26 additions & 12 deletions src/archive/tar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use std::{
env,
io::prelude::*,
path::{Path, PathBuf},
sync::mpsc::{self, Receiver},
thread,
};

use fs_err as fs;
Expand Down Expand Up @@ -48,20 +50,32 @@ pub fn unpack_archive(
}

/// List contents of `archive`, returning a vector of archive entries
pub fn list_archive(reader: Box<dyn Read>) -> crate::Result<Vec<FileInArchive>> {
let mut archive = tar::Archive::new(reader);

let mut files = vec![];
for file in archive.entries()? {
let file = file?;

let path = file.path()?.into_owned();
let is_dir = file.header().entry_type().is_dir();

files.push(FileInArchive { path, is_dir });
pub fn list_archive(
mut archive: tar::Archive<impl Read + Send + 'static>,
) -> impl Iterator<Item = crate::Result<FileInArchive>> {
struct Files(Receiver<crate::Result<FileInArchive>>);
impl Iterator for Files {
type Item = crate::Result<FileInArchive>;

fn next(&mut self) -> Option<Self::Item> {
self.0.recv().ok()
}
}

Ok(files)
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();
}
});

Files(rx)
}

/// Compresses the archives given by `input_filenames` into the file given previously to `writer`.
Expand Down
48 changes: 35 additions & 13 deletions src/archive/zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use std::{
env,
io::{self, prelude::*},
path::{Path, PathBuf},
sync::mpsc,
thread,
};

use fs_err as fs;
Expand Down Expand Up @@ -77,23 +79,43 @@ where
}

/// List contents of `archive`, returning a vector of archive entries
pub fn list_archive<R>(mut archive: ZipArchive<R>) -> crate::Result<Vec<FileInArchive>>
pub fn list_archive<R>(mut archive: ZipArchive<R>) -> impl Iterator<Item = crate::Result<FileInArchive>>
where
R: Read + Seek,
R: Read + Seek + Send + 'static,
{
let mut files = vec![];
for idx in 0..archive.len() {
let file = archive.by_index(idx)?;

let path = match file.enclosed_name() {
Some(path) => path.to_owned(),
None => continue,
};
let is_dir = file.is_dir();
struct Files(mpsc::Receiver<crate::Result<FileInArchive>>);
impl Iterator for Files {
type Item = crate::Result<FileInArchive>;

files.push(FileInArchive { path, is_dir });
fn next(&mut self) -> Option<Self::Item> {
self.0.recv().ok()
}
}
Ok(files)

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`.
Expand Down
40 changes: 21 additions & 19 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
Extension,
},
info,
list::{self, ListOptions},
list::{self, FileInArchive, ListOptions},
progress::Progress,
utils::{
self, concatenate_os_str_list, dir_is_empty, nice_directory_display, to_utf, try_infer_extension,
Expand Down Expand Up @@ -580,35 +580,37 @@ fn list_archive_contents(
// Any other Zip decompression done can take up the whole RAM and freeze ouch.
if let [Zip] = *formats.as_slice() {
let zip_archive = zip::ZipArchive::new(reader)?;
let files = crate::archive::zip::list_archive(zip_archive)?;
list::list_files(archive_path, files, list_options);
let files = crate::archive::zip::list_archive(zip_archive);
list::list_files(archive_path, files, list_options)?;

return Ok(());
}

// Will be used in decoder chaining
let reader = BufReader::with_capacity(BUFFER_CAPACITY, reader);
let mut reader: Box<dyn Read> = Box::new(reader);
let mut reader: Box<dyn Read + Send> = Box::new(reader);

// Grab previous decoder and wrap it inside of a new one
let chain_reader_decoder = |format: &CompressionFormat, decoder: Box<dyn Read>| -> crate::Result<Box<dyn Read>> {
let decoder: Box<dyn Read> = 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<dyn Read + Send>| -> crate::Result<Box<dyn Read + Send>> {
let decoder: Box<dyn Read + Send> = 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)
};

for format in formats.iter().skip(1).rev() {
reader = chain_reader_decoder(format, reader)?;
}

let files = match formats[0] {
Tar => crate::archive::tar::list_archive(reader)?,
let files: Box<dyn Iterator<Item = crate::Result<FileInArchive>>> = match formats[0] {
Tar => Box::new(crate::archive::tar::list_archive(tar::Archive::new(reader))),
Zip => {
eprintln!("{orange}[WARNING]{reset}", orange = *colors::ORANGE, reset = *colors::RESET);
eprintln!(
Expand All @@ -626,13 +628,13 @@ fn list_archive_contents(
io::copy(&mut reader, &mut vec)?;
let zip_archive = zip::ZipArchive::new(io::Cursor::new(vec))?;

crate::archive::zip::list_archive(zip_archive)?
Box::new(crate::archive::zip::list_archive(zip_archive))
}
Gzip | Bzip | Lz4 | Lzma | Snappy | Zstd => {
panic!("Not an archive! This should never happen, if it does, something is wrong with `CompressionFormat::is_archive()`. Please report this error!");
}
};
list::list_files(archive_path, files, list_options);
list::list_files(archive_path, files, list_options)?;
Ok(())
}

Expand Down
35 changes: 32 additions & 3 deletions src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

use std::path::{Path, PathBuf};

use indicatif::{ProgressBar, ProgressStyle};

use self::tree::Tree;

/// Options controlling how archive contents should be listed
Expand All @@ -22,16 +24,43 @@ pub struct FileInArchive {
}

/// Actually print the files
pub fn list_files(archive: &Path, files: Vec<FileInArchive>, list_options: ListOptions) {
/// Returns an Error, if one of the files can't be read
pub fn list_files(
archive: &Path,
files: impl IntoIterator<Item = crate::Result<FileInArchive>>,
list_options: ListOptions,
) -> crate::Result<()> {
println!("Archive: {}", archive.display());

if list_options.tree {
let tree: Tree = files.into_iter().collect();
let pb = if !crate::cli::ACCESSIBLE.get().unwrap() {
let template = "{wide_msg} [{elapsed_precise}] {spinner:.green}";
let pb = ProgressBar::new_spinner();
pb.set_style(ProgressStyle::default_bar().template(template));
Some(pb)
} else {
None
};

let tree: Tree = files
.into_iter()
.map(|file| {
let file = file?;
if !crate::cli::ACCESSIBLE.get().unwrap() {
pb.as_ref().expect("exists").set_message(format!("Processing: {}", file.path.display()));
}
Ok(file)
})
.collect::<crate::Result<Tree>>()?;
drop(pb);
tree.print();
} else {
for FileInArchive { path, is_dir } in files {
for file in files {
let FileInArchive { path, is_dir } = file?;
print_entry(path.display(), is_dir);
}
}
Ok(())
}

/// Print an entry and highlight directories, either by coloring them
Expand Down

0 comments on commit 308b8f7

Please sign in to comment.