mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-06 19:45:29 +00:00
Added buffering
This commit is contained in:
parent
c2873f77d3
commit
e989db7a3a
@ -21,7 +21,12 @@ use crate::{
|
|||||||
|
|
||||||
/// Unpacks the archive given by `archive` into the folder given by `into`.
|
/// Unpacks the archive given by `archive` into the folder given by `into`.
|
||||||
/// Assumes that output_folder is empty
|
/// Assumes that output_folder is empty
|
||||||
pub fn unpack_archive(reader: Box<dyn Read>, output_folder: &Path, quiet: bool, log_sender: Sender<PrintMessage>) -> crate::Result<usize> {
|
pub fn unpack_archive(
|
||||||
|
reader: Box<dyn Read>,
|
||||||
|
output_folder: &Path,
|
||||||
|
quiet: bool,
|
||||||
|
log_sender: Sender<PrintMessage>,
|
||||||
|
) -> crate::Result<usize> {
|
||||||
assert!(output_folder.read_dir().expect("dir exists").count() == 0);
|
assert!(output_folder.read_dir().expect("dir exists").count() == 0);
|
||||||
let mut archive = tar::Archive::new(reader);
|
let mut archive = tar::Archive::new(reader);
|
||||||
|
|
||||||
@ -36,14 +41,16 @@ pub fn unpack_archive(reader: Box<dyn Read>, output_folder: &Path, quiet: bool,
|
|||||||
// spoken text for users using screen readers, braille displays
|
// spoken text for users using screen readers, braille displays
|
||||||
// and so on
|
// and so on
|
||||||
if !quiet {
|
if !quiet {
|
||||||
log_sender.send(PrintMessage {
|
log_sender
|
||||||
contents: format!(
|
.send(PrintMessage {
|
||||||
"{:?} extracted. ({})",
|
contents: format!(
|
||||||
utils::strip_cur_dir(&output_folder.join(file.path()?)),
|
"{:?} extracted. ({})",
|
||||||
Bytes::new(file.size()),
|
utils::strip_cur_dir(&output_folder.join(file.path()?)),
|
||||||
),
|
Bytes::new(file.size()),
|
||||||
accessible: false
|
),
|
||||||
}).unwrap();
|
accessible: false,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
files_unpacked += 1;
|
files_unpacked += 1;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use std::{
|
|||||||
env,
|
env,
|
||||||
io::{self, prelude::*},
|
io::{self, prelude::*},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::mpsc,
|
sync::mpsc::{self, Sender},
|
||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,15 +21,20 @@ use crate::{
|
|||||||
info,
|
info,
|
||||||
list::FileInArchive,
|
list::FileInArchive,
|
||||||
utils::{
|
utils::{
|
||||||
self, cd_into_same_dir_as, get_invalid_utf8_paths, pretty_format_list_of_paths, strip_cur_dir, Bytes,
|
self, cd_into_same_dir_as, get_invalid_utf8_paths, io::PrintMessage, pretty_format_list_of_paths,
|
||||||
EscapedPathDisplay, FileVisibilityPolicy,
|
strip_cur_dir, Bytes, EscapedPathDisplay, FileVisibilityPolicy,
|
||||||
},
|
},
|
||||||
warning,
|
warning,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Unpacks the archive given by `archive` into the folder given by `output_folder`.
|
/// Unpacks the archive given by `archive` into the folder given by `output_folder`.
|
||||||
/// Assumes that output_folder is empty
|
/// Assumes that output_folder is empty
|
||||||
pub fn unpack_archive<R>(mut archive: ZipArchive<R>, output_folder: &Path, quiet: bool) -> crate::Result<usize>
|
pub fn unpack_archive<R>(
|
||||||
|
mut archive: ZipArchive<R>,
|
||||||
|
output_folder: &Path,
|
||||||
|
quiet: bool,
|
||||||
|
log_sender: Sender<PrintMessage>,
|
||||||
|
) -> crate::Result<usize>
|
||||||
where
|
where
|
||||||
R: Read + Seek,
|
R: Read + Seek,
|
||||||
{
|
{
|
||||||
@ -55,7 +60,12 @@ where
|
|||||||
// spoken text for users using screen readers, braille displays
|
// spoken text for users using screen readers, braille displays
|
||||||
// and so on
|
// and so on
|
||||||
if !quiet {
|
if !quiet {
|
||||||
info!(inaccessible, "File {} extracted to \"{}\"", idx, file_path.display());
|
log_sender
|
||||||
|
.send(PrintMessage {
|
||||||
|
contents: format!("File {} extracted to \"{}\"", idx, file_path.display()),
|
||||||
|
accessible: false,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
fs::create_dir_all(&file_path)?;
|
fs::create_dir_all(&file_path)?;
|
||||||
}
|
}
|
||||||
@ -69,12 +79,12 @@ where
|
|||||||
|
|
||||||
// same reason is in _is_dir: long, often not needed text
|
// same reason is in _is_dir: long, often not needed text
|
||||||
if !quiet {
|
if !quiet {
|
||||||
info!(
|
log_sender
|
||||||
inaccessible,
|
.send(PrintMessage {
|
||||||
"{:?} extracted. ({})",
|
contents: format!("{:?} extracted. ({})", file_path.display(), Bytes::new(file.size()),),
|
||||||
file_path.display(),
|
accessible: false,
|
||||||
Bytes::new(file.size()),
|
})
|
||||||
);
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut output_file = fs::File::create(file_path)?;
|
let mut output_file = fs::File::create(file_path)?;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use std::{
|
use std::{
|
||||||
io::{self, BufReader, Read},
|
io::{self, BufReader, Read},
|
||||||
ops::ControlFlow,
|
ops::ControlFlow,
|
||||||
path::{Path, PathBuf}, sync::mpsc::Sender,
|
path::{Path, PathBuf},
|
||||||
|
sync::mpsc::Sender,
|
||||||
};
|
};
|
||||||
|
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
@ -50,7 +51,7 @@ pub fn decompress_file(
|
|||||||
{
|
{
|
||||||
let zip_archive = zip::ZipArchive::new(reader)?;
|
let zip_archive = zip::ZipArchive::new(reader)?;
|
||||||
let files_unpacked = if let ControlFlow::Continue(files) = smart_unpack(
|
let files_unpacked = if let ControlFlow::Continue(files) = smart_unpack(
|
||||||
|output_dir| crate::archive::zip::unpack_archive(zip_archive, output_dir, quiet),
|
|output_dir| crate::archive::zip::unpack_archive(zip_archive, output_dir, quiet, log_sender.clone()),
|
||||||
output_dir,
|
output_dir,
|
||||||
&output_file_path,
|
&output_file_path,
|
||||||
question_policy,
|
question_policy,
|
||||||
@ -65,14 +66,16 @@ pub fn decompress_file(
|
|||||||
// having a final status message is important especially in an accessibility context
|
// having a final status message is important especially in an accessibility context
|
||||||
// as screen readers may not read a commands exit code, making it hard to reason
|
// as screen readers may not read a commands exit code, making it hard to reason
|
||||||
// about whether the command succeeded without such a message
|
// about whether the command succeeded without such a message
|
||||||
log_sender.send(PrintMessage {
|
log_sender
|
||||||
contents: format!(
|
.send(PrintMessage {
|
||||||
"Successfully decompressed archive in {} ({} files).",
|
contents: format!(
|
||||||
nice_directory_display(output_dir),
|
"Successfully decompressed archive in {} ({} files).",
|
||||||
files_unpacked
|
nice_directory_display(output_dir),
|
||||||
),
|
files_unpacked
|
||||||
accessible: true
|
),
|
||||||
}).unwrap();
|
accessible: true,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -141,7 +144,7 @@ pub fn decompress_file(
|
|||||||
let zip_archive = zip::ZipArchive::new(io::Cursor::new(vec))?;
|
let zip_archive = zip::ZipArchive::new(io::Cursor::new(vec))?;
|
||||||
|
|
||||||
if let ControlFlow::Continue(files) = smart_unpack(
|
if let ControlFlow::Continue(files) = smart_unpack(
|
||||||
|output_dir| crate::archive::zip::unpack_archive(zip_archive, output_dir, quiet),
|
|output_dir| crate::archive::zip::unpack_archive(zip_archive, output_dir, quiet, log_sender.clone()),
|
||||||
output_dir,
|
output_dir,
|
||||||
&output_file_path,
|
&output_file_path,
|
||||||
question_policy,
|
question_policy,
|
||||||
@ -163,9 +166,13 @@ pub fn decompress_file(
|
|||||||
Box::new(|output_dir| crate::archive::rar::unpack_archive(input_file_path, output_dir, quiet))
|
Box::new(|output_dir| crate::archive::rar::unpack_archive(input_file_path, output_dir, quiet))
|
||||||
};
|
};
|
||||||
|
|
||||||
if let ControlFlow::Continue(files) =
|
if let ControlFlow::Continue(files) = smart_unpack(
|
||||||
smart_unpack(unpack_fn, output_dir, &output_file_path, question_policy, log_sender.clone())?
|
unpack_fn,
|
||||||
{
|
output_dir,
|
||||||
|
&output_file_path,
|
||||||
|
question_policy,
|
||||||
|
log_sender.clone(),
|
||||||
|
)? {
|
||||||
files
|
files
|
||||||
} else {
|
} else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -205,17 +212,21 @@ pub fn decompress_file(
|
|||||||
// having a final status message is important especially in an accessibility context
|
// having a final status message is important especially in an accessibility context
|
||||||
// as screen readers may not read a commands exit code, making it hard to reason
|
// as screen readers may not read a commands exit code, making it hard to reason
|
||||||
// about whether the command succeeded without such a message
|
// about whether the command succeeded without such a message
|
||||||
log_sender.send(PrintMessage {
|
log_sender
|
||||||
contents: format!(
|
.send(PrintMessage {
|
||||||
"Successfully decompressed archive in {}.",
|
contents: format!(
|
||||||
nice_directory_display(output_dir)
|
"Successfully decompressed archive in {}.",
|
||||||
),
|
nice_directory_display(output_dir)
|
||||||
accessible: true
|
),
|
||||||
}).unwrap();
|
accessible: true,
|
||||||
log_sender.send(PrintMessage {
|
})
|
||||||
contents: format!("Files unpacked: {}", files_unpacked),
|
.unwrap();
|
||||||
accessible: true
|
log_sender
|
||||||
}).unwrap();
|
.send(PrintMessage {
|
||||||
|
contents: format!("Files unpacked: {}", files_unpacked),
|
||||||
|
accessible: true,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -236,13 +247,15 @@ fn smart_unpack(
|
|||||||
let temp_dir = tempfile::tempdir_in(output_dir)?;
|
let temp_dir = tempfile::tempdir_in(output_dir)?;
|
||||||
let temp_dir_path = temp_dir.path();
|
let temp_dir_path = temp_dir.path();
|
||||||
|
|
||||||
log_sender.send(PrintMessage {
|
log_sender
|
||||||
contents: format!(
|
.send(PrintMessage {
|
||||||
"Created temporary directory {} to hold decompressed elements.",
|
contents: format!(
|
||||||
nice_directory_display(temp_dir_path)
|
"Created temporary directory {} to hold decompressed elements.",
|
||||||
),
|
nice_directory_display(temp_dir_path)
|
||||||
accessible: true
|
),
|
||||||
}).unwrap();
|
accessible: true,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let files = unpack_fn(temp_dir_path)?;
|
let files = unpack_fn(temp_dir_path)?;
|
||||||
|
|
||||||
@ -261,14 +274,16 @@ fn smart_unpack(
|
|||||||
}
|
}
|
||||||
fs::rename(&file_path, &correct_path)?;
|
fs::rename(&file_path, &correct_path)?;
|
||||||
|
|
||||||
log_sender.send(PrintMessage {
|
log_sender
|
||||||
contents: format!(
|
.send(PrintMessage {
|
||||||
"Successfully moved {} to {}.",
|
contents: format!(
|
||||||
nice_directory_display(&file_path),
|
"Successfully moved {} to {}.",
|
||||||
nice_directory_display(&correct_path)
|
nice_directory_display(&file_path),
|
||||||
),
|
nice_directory_display(&correct_path)
|
||||||
accessible: true
|
),
|
||||||
}).unwrap();
|
accessible: true,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
} else {
|
} else {
|
||||||
// Multiple files in the root directory, so:
|
// Multiple files in the root directory, so:
|
||||||
// Rename the temporary directory to the archive name, which is output_file_path
|
// Rename the temporary directory to the archive name, which is output_file_path
|
||||||
@ -277,14 +292,16 @@ fn smart_unpack(
|
|||||||
return Ok(ControlFlow::Break(()));
|
return Ok(ControlFlow::Break(()));
|
||||||
}
|
}
|
||||||
fs::rename(temp_dir_path, output_file_path)?;
|
fs::rename(temp_dir_path, output_file_path)?;
|
||||||
log_sender.send(PrintMessage {
|
log_sender
|
||||||
contents: format!(
|
.send(PrintMessage {
|
||||||
"Successfully moved {} to {}.",
|
contents: format!(
|
||||||
nice_directory_display(temp_dir_path),
|
"Successfully moved {} to {}.",
|
||||||
nice_directory_display(output_file_path)
|
nice_directory_display(temp_dir_path),
|
||||||
),
|
nice_directory_display(output_file_path)
|
||||||
accessible: true
|
),
|
||||||
}).unwrap();
|
accessible: true,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ControlFlow::Continue(files))
|
Ok(ControlFlow::Continue(files))
|
||||||
|
@ -4,7 +4,11 @@ mod compress;
|
|||||||
mod decompress;
|
mod decompress;
|
||||||
mod list;
|
mod list;
|
||||||
|
|
||||||
use std::{ops::ControlFlow, path::PathBuf, sync::{mpsc::channel, Arc, Condvar, Mutex}};
|
use std::{
|
||||||
|
ops::ControlFlow,
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{mpsc::channel, Arc, Condvar, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
||||||
use utils::colors;
|
use utils::colors;
|
||||||
@ -176,11 +180,21 @@ pub fn run(
|
|||||||
|
|
||||||
// Log received messages until all senders are dropped
|
// Log received messages until all senders are dropped
|
||||||
rayon::spawn(move || {
|
rayon::spawn(move || {
|
||||||
|
let mut buffer = Vec::<String>::with_capacity(10);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let msg = log_receiver.recv();
|
let msg = log_receiver.recv();
|
||||||
if let Ok(msg) = msg {
|
if let Ok(msg) = msg {
|
||||||
println!("{}", msg.contents);
|
if buffer.len() == 10 {
|
||||||
|
let mut tmp = buffer.join("\n");
|
||||||
|
tmp.push_str(&msg.contents);
|
||||||
|
println!("{}", tmp);
|
||||||
|
buffer.clear();
|
||||||
|
} else {
|
||||||
|
buffer.push(msg.contents);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
println!("{}", buffer.join("\n"));
|
||||||
let (lock, cvar) = &*pair2;
|
let (lock, cvar) = &*pair2;
|
||||||
let mut flushed = lock.lock().unwrap();
|
let mut flushed = lock.lock().unwrap();
|
||||||
*flushed = true;
|
*flushed = true;
|
||||||
@ -207,16 +221,16 @@ pub fn run(
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Drop our sender clones so when all threads are done, no clones are left
|
// Drop our sender clones so when all threads are done, no clones are left
|
||||||
drop(log_sender);
|
drop(log_sender);
|
||||||
|
|
||||||
// Prevent the main thread from exiting until the background thread handling the
|
// Prevent the main thread from exiting until the background thread handling the
|
||||||
// logging has set `flushed` to true.
|
// logging has set `flushed` to true.
|
||||||
let (lock, cvar) = &*pair;
|
let (lock, cvar) = &*pair;
|
||||||
let mut flushed = lock.lock().unwrap();
|
let mut flushed = lock.lock().unwrap();
|
||||||
while !*flushed {
|
while !*flushed {
|
||||||
flushed = cvar.wait(flushed).unwrap();
|
flushed = cvar.wait(flushed).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Subcommand::List { archives: files, tree } => {
|
Subcommand::List { archives: files, tree } => {
|
||||||
let mut formats = vec![];
|
let mut formats = vec![];
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PrintMessage {
|
pub struct PrintMessage {
|
||||||
pub contents: String,
|
pub contents: String,
|
||||||
pub accessible: bool,
|
pub accessible: bool,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user