mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-07 12:05:46 +00:00
Merge pull request #225 from sigmaSd/list
List command: print file immediatly after it is processed
This commit is contained in:
commit
308b8f7e90
@ -4,6 +4,8 @@ use std::{
|
|||||||
env,
|
env,
|
||||||
io::prelude::*,
|
io::prelude::*,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
sync::mpsc::{self, Receiver},
|
||||||
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
@ -48,20 +50,32 @@ pub fn unpack_archive(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// List contents of `archive`, returning a vector of archive entries
|
/// List contents of `archive`, returning a vector of archive entries
|
||||||
pub fn list_archive(reader: Box<dyn Read>) -> crate::Result<Vec<FileInArchive>> {
|
pub fn list_archive(
|
||||||
let mut archive = tar::Archive::new(reader);
|
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>;
|
||||||
|
|
||||||
let mut files = vec![];
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
for file in archive.entries()? {
|
self.0.recv().ok()
|
||||||
let file = file?;
|
}
|
||||||
|
|
||||||
let path = file.path()?.into_owned();
|
|
||||||
let is_dir = file.header().entry_type().is_dir();
|
|
||||||
|
|
||||||
files.push(FileInArchive { path, is_dir });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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`.
|
/// Compresses the archives given by `input_filenames` into the file given previously to `writer`.
|
||||||
|
@ -4,6 +4,8 @@ use std::{
|
|||||||
env,
|
env,
|
||||||
io::{self, prelude::*},
|
io::{self, prelude::*},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
sync::mpsc,
|
||||||
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
@ -77,23 +79,43 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// List contents of `archive`, returning a vector of archive entries
|
/// 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
|
where
|
||||||
R: Read + Seek,
|
R: Read + Seek + Send + 'static,
|
||||||
{
|
{
|
||||||
let mut files = vec![];
|
struct Files(mpsc::Receiver<crate::Result<FileInArchive>>);
|
||||||
for idx in 0..archive.len() {
|
impl Iterator for Files {
|
||||||
let file = archive.by_index(idx)?;
|
type Item = crate::Result<FileInArchive>;
|
||||||
|
|
||||||
let path = match file.enclosed_name() {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
Some(path) => path.to_owned(),
|
self.0.recv().ok()
|
||||||
None => continue,
|
}
|
||||||
};
|
|
||||||
let is_dir = file.is_dir();
|
|
||||||
|
|
||||||
files.push(FileInArchive { path, is_dir });
|
|
||||||
}
|
}
|
||||||
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`.
|
/// Compresses the archives given by `input_filenames` into the file given previously to `writer`.
|
||||||
|
@ -20,7 +20,7 @@ use crate::{
|
|||||||
Extension,
|
Extension,
|
||||||
},
|
},
|
||||||
info,
|
info,
|
||||||
list::{self, ListOptions},
|
list::{self, FileInArchive, ListOptions},
|
||||||
progress::Progress,
|
progress::Progress,
|
||||||
utils::{
|
utils::{
|
||||||
self, concatenate_os_str_list, dir_is_empty, nice_directory_display, to_utf, try_infer_extension,
|
self, concatenate_os_str_list, dir_is_empty, nice_directory_display, to_utf, try_infer_extension,
|
||||||
@ -580,35 +580,37 @@ fn list_archive_contents(
|
|||||||
// Any other Zip decompression done can take up the whole RAM and freeze ouch.
|
// Any other Zip decompression done can take up the whole RAM and freeze ouch.
|
||||||
if let [Zip] = *formats.as_slice() {
|
if let [Zip] = *formats.as_slice() {
|
||||||
let zip_archive = zip::ZipArchive::new(reader)?;
|
let zip_archive = zip::ZipArchive::new(reader)?;
|
||||||
let files = crate::archive::zip::list_archive(zip_archive)?;
|
let files = crate::archive::zip::list_archive(zip_archive);
|
||||||
list::list_files(archive_path, files, list_options);
|
list::list_files(archive_path, files, list_options)?;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will be used in decoder chaining
|
// Will be used in decoder chaining
|
||||||
let reader = BufReader::with_capacity(BUFFER_CAPACITY, reader);
|
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
|
// 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 chain_reader_decoder =
|
||||||
let decoder: Box<dyn Read> = match format {
|
|format: &CompressionFormat, decoder: Box<dyn Read + Send>| -> crate::Result<Box<dyn Read + Send>> {
|
||||||
Gzip => Box::new(flate2::read::GzDecoder::new(decoder)),
|
let decoder: Box<dyn Read + Send> = match format {
|
||||||
Bzip => Box::new(bzip2::read::BzDecoder::new(decoder)),
|
Gzip => Box::new(flate2::read::GzDecoder::new(decoder)),
|
||||||
Lz4 => Box::new(lzzzz::lz4f::ReadDecompressor::new(decoder)?),
|
Bzip => Box::new(bzip2::read::BzDecoder::new(decoder)),
|
||||||
Lzma => Box::new(xz2::read::XzDecoder::new(decoder)),
|
Lz4 => Box::new(lzzzz::lz4f::ReadDecompressor::new(decoder)?),
|
||||||
Snappy => Box::new(snap::read::FrameDecoder::new(decoder)),
|
Lzma => Box::new(xz2::read::XzDecoder::new(decoder)),
|
||||||
Zstd => Box::new(zstd::stream::Decoder::new(decoder)?),
|
Snappy => Box::new(snap::read::FrameDecoder::new(decoder)),
|
||||||
Tar | Zip => unreachable!(),
|
Zstd => Box::new(zstd::stream::Decoder::new(decoder)?),
|
||||||
|
Tar | Zip => unreachable!(),
|
||||||
|
};
|
||||||
|
Ok(decoder)
|
||||||
};
|
};
|
||||||
Ok(decoder)
|
|
||||||
};
|
|
||||||
|
|
||||||
for format in formats.iter().skip(1).rev() {
|
for format in formats.iter().skip(1).rev() {
|
||||||
reader = chain_reader_decoder(format, reader)?;
|
reader = chain_reader_decoder(format, reader)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let files = match formats[0] {
|
let files: Box<dyn Iterator<Item = crate::Result<FileInArchive>>> = match formats[0] {
|
||||||
Tar => crate::archive::tar::list_archive(reader)?,
|
Tar => Box::new(crate::archive::tar::list_archive(tar::Archive::new(reader))),
|
||||||
Zip => {
|
Zip => {
|
||||||
eprintln!("{orange}[WARNING]{reset}", orange = *colors::ORANGE, reset = *colors::RESET);
|
eprintln!("{orange}[WARNING]{reset}", orange = *colors::ORANGE, reset = *colors::RESET);
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -626,13 +628,13 @@ fn list_archive_contents(
|
|||||||
io::copy(&mut reader, &mut vec)?;
|
io::copy(&mut reader, &mut vec)?;
|
||||||
let zip_archive = zip::ZipArchive::new(io::Cursor::new(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 => {
|
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!");
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
src/list.rs
35
src/list.rs
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
|
|
||||||
use self::tree::Tree;
|
use self::tree::Tree;
|
||||||
|
|
||||||
/// Options controlling how archive contents should be listed
|
/// Options controlling how archive contents should be listed
|
||||||
@ -22,16 +24,43 @@ pub struct FileInArchive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Actually print the files
|
/// 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());
|
println!("Archive: {}", archive.display());
|
||||||
|
|
||||||
if list_options.tree {
|
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();
|
tree.print();
|
||||||
} else {
|
} else {
|
||||||
for FileInArchive { path, is_dir } in files {
|
for file in files {
|
||||||
|
let FileInArchive { path, is_dir } = file?;
|
||||||
print_entry(path.display(), is_dir);
|
print_entry(path.display(), is_dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print an entry and highlight directories, either by coloring them
|
/// Print an entry and highlight directories, either by coloring them
|
||||||
|
Loading…
x
Reference in New Issue
Block a user