mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-08 20:45:29 +00:00
wip
This commit is contained in:
parent
5b0e0b6991
commit
3a3f38c93e
@ -4,7 +4,7 @@ use std::{
|
||||
env,
|
||||
io::prelude::*,
|
||||
path::{Path, PathBuf},
|
||||
sync::mpsc::{self, Receiver},
|
||||
sync::mpsc::{self, Receiver, Sender},
|
||||
thread,
|
||||
};
|
||||
|
||||
@ -15,13 +15,13 @@ use crate::{
|
||||
error::FinalError,
|
||||
info,
|
||||
list::FileInArchive,
|
||||
utils::{self, Bytes, EscapedPathDisplay, FileVisibilityPolicy},
|
||||
utils::{self, io::PrintMessage, Bytes, EscapedPathDisplay, FileVisibilityPolicy},
|
||||
warning,
|
||||
};
|
||||
|
||||
/// Unpacks the archive given by `archive` into the folder given by `into`.
|
||||
/// Assumes that output_folder is empty
|
||||
pub fn unpack_archive(reader: Box<dyn Read>, output_folder: &Path, quiet: bool) -> 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);
|
||||
let mut archive = tar::Archive::new(reader);
|
||||
|
||||
@ -36,12 +36,14 @@ pub fn unpack_archive(reader: Box<dyn Read>, output_folder: &Path, quiet: bool)
|
||||
// spoken text for users using screen readers, braille displays
|
||||
// and so on
|
||||
if !quiet {
|
||||
info!(
|
||||
inaccessible,
|
||||
"{:?} extracted. ({})",
|
||||
utils::strip_cur_dir(&output_folder.join(file.path()?)),
|
||||
Bytes::new(file.size()),
|
||||
);
|
||||
log_sender.send(PrintMessage {
|
||||
contents: format!(
|
||||
"{:?} extracted. ({})",
|
||||
utils::strip_cur_dir(&output_folder.join(file.path()?)),
|
||||
Bytes::new(file.size()),
|
||||
),
|
||||
accessible: false
|
||||
}).unwrap();
|
||||
|
||||
files_unpacked += 1;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::{
|
||||
io::{self, BufReader, Read},
|
||||
ops::ControlFlow,
|
||||
path::{Path, PathBuf},
|
||||
path::{Path, PathBuf}, sync::mpsc::Sender,
|
||||
};
|
||||
|
||||
use fs_err as fs;
|
||||
@ -14,7 +14,7 @@ use crate::{
|
||||
Extension,
|
||||
},
|
||||
info,
|
||||
utils::{self, nice_directory_display, user_wants_to_continue},
|
||||
utils::{self, io::PrintMessage, nice_directory_display, user_wants_to_continue},
|
||||
QuestionAction, QuestionPolicy, BUFFER_CAPACITY,
|
||||
};
|
||||
|
||||
@ -31,6 +31,7 @@ pub fn decompress_file(
|
||||
output_file_path: PathBuf,
|
||||
question_policy: QuestionPolicy,
|
||||
quiet: bool,
|
||||
log_sender: Sender<PrintMessage>,
|
||||
) -> crate::Result<()> {
|
||||
assert!(output_dir.exists());
|
||||
let reader = fs::File::open(input_file_path)?;
|
||||
@ -53,6 +54,7 @@ pub fn decompress_file(
|
||||
output_dir,
|
||||
&output_file_path,
|
||||
question_policy,
|
||||
log_sender.clone(),
|
||||
)? {
|
||||
files
|
||||
} else {
|
||||
@ -63,12 +65,14 @@ pub fn decompress_file(
|
||||
// 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
|
||||
// about whether the command succeeded without such a message
|
||||
info!(
|
||||
accessible,
|
||||
"Successfully decompressed archive in {} ({} files).",
|
||||
nice_directory_display(output_dir),
|
||||
files_unpacked
|
||||
);
|
||||
log_sender.send(PrintMessage {
|
||||
contents: format!(
|
||||
"Successfully decompressed archive in {} ({} files).",
|
||||
nice_directory_display(output_dir),
|
||||
files_unpacked
|
||||
),
|
||||
accessible: true
|
||||
}).unwrap();
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
@ -112,10 +116,11 @@ pub fn decompress_file(
|
||||
}
|
||||
Tar => {
|
||||
if let ControlFlow::Continue(files) = smart_unpack(
|
||||
|output_dir| crate::archive::tar::unpack_archive(reader, output_dir, quiet),
|
||||
|output_dir| crate::archive::tar::unpack_archive(reader, output_dir, quiet, log_sender.clone()),
|
||||
output_dir,
|
||||
&output_file_path,
|
||||
question_policy,
|
||||
log_sender.clone(),
|
||||
)? {
|
||||
files
|
||||
} else {
|
||||
@ -140,6 +145,7 @@ pub fn decompress_file(
|
||||
output_dir,
|
||||
&output_file_path,
|
||||
question_policy,
|
||||
log_sender.clone(),
|
||||
)? {
|
||||
files
|
||||
} else {
|
||||
@ -158,7 +164,7 @@ pub fn decompress_file(
|
||||
};
|
||||
|
||||
if let ControlFlow::Continue(files) =
|
||||
smart_unpack(unpack_fn, output_dir, &output_file_path, question_policy)?
|
||||
smart_unpack(unpack_fn, output_dir, &output_file_path, question_policy, log_sender.clone())?
|
||||
{
|
||||
files
|
||||
} else {
|
||||
@ -186,6 +192,7 @@ pub fn decompress_file(
|
||||
output_dir,
|
||||
&output_file_path,
|
||||
question_policy,
|
||||
log_sender.clone(),
|
||||
)? {
|
||||
files
|
||||
} else {
|
||||
@ -198,12 +205,17 @@ pub fn decompress_file(
|
||||
// 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
|
||||
// about whether the command succeeded without such a message
|
||||
info!(
|
||||
accessible,
|
||||
"Successfully decompressed archive in {}.",
|
||||
nice_directory_display(output_dir)
|
||||
);
|
||||
info!(accessible, "Files unpacked: {}", files_unpacked);
|
||||
log_sender.send(PrintMessage {
|
||||
contents: format!(
|
||||
"Successfully decompressed archive in {}.",
|
||||
nice_directory_display(output_dir)
|
||||
),
|
||||
accessible: true
|
||||
}).unwrap();
|
||||
log_sender.send(PrintMessage {
|
||||
contents: format!("Files unpacked: {}", files_unpacked),
|
||||
accessible: true
|
||||
}).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -218,15 +230,19 @@ fn smart_unpack(
|
||||
output_dir: &Path,
|
||||
output_file_path: &Path,
|
||||
question_policy: QuestionPolicy,
|
||||
log_sender: Sender<PrintMessage>,
|
||||
) -> crate::Result<ControlFlow<(), usize>> {
|
||||
assert!(output_dir.exists());
|
||||
let temp_dir = tempfile::tempdir_in(output_dir)?;
|
||||
let temp_dir_path = temp_dir.path();
|
||||
info!(
|
||||
accessible,
|
||||
"Created temporary directory {} to hold decompressed elements.",
|
||||
nice_directory_display(temp_dir_path)
|
||||
);
|
||||
|
||||
log_sender.send(PrintMessage {
|
||||
contents: format!(
|
||||
"Created temporary directory {} to hold decompressed elements.",
|
||||
nice_directory_display(temp_dir_path)
|
||||
),
|
||||
accessible: true
|
||||
}).unwrap();
|
||||
|
||||
let files = unpack_fn(temp_dir_path)?;
|
||||
|
||||
@ -244,12 +260,15 @@ fn smart_unpack(
|
||||
return Ok(ControlFlow::Break(()));
|
||||
}
|
||||
fs::rename(&file_path, &correct_path)?;
|
||||
info!(
|
||||
accessible,
|
||||
"Successfully moved {} to {}.",
|
||||
nice_directory_display(&file_path),
|
||||
nice_directory_display(&correct_path)
|
||||
);
|
||||
|
||||
log_sender.send(PrintMessage {
|
||||
contents: format!(
|
||||
"Successfully moved {} to {}.",
|
||||
nice_directory_display(&file_path),
|
||||
nice_directory_display(&correct_path)
|
||||
),
|
||||
accessible: true
|
||||
}).unwrap();
|
||||
} else {
|
||||
// Multiple files in the root directory, so:
|
||||
// Rename the temporary directory to the archive name, which is output_file_path
|
||||
@ -258,12 +277,14 @@ fn smart_unpack(
|
||||
return Ok(ControlFlow::Break(()));
|
||||
}
|
||||
fs::rename(temp_dir_path, output_file_path)?;
|
||||
info!(
|
||||
accessible,
|
||||
"Successfully moved {} to {}.",
|
||||
nice_directory_display(temp_dir_path),
|
||||
nice_directory_display(output_file_path)
|
||||
);
|
||||
log_sender.send(PrintMessage {
|
||||
contents: format!(
|
||||
"Successfully moved {} to {}.",
|
||||
nice_directory_display(temp_dir_path),
|
||||
nice_directory_display(output_file_path)
|
||||
),
|
||||
accessible: true
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
Ok(ControlFlow::Continue(files))
|
||||
|
@ -4,7 +4,7 @@ mod compress;
|
||||
mod decompress;
|
||||
mod list;
|
||||
|
||||
use std::{ops::ControlFlow, path::PathBuf};
|
||||
use std::{ops::ControlFlow, path::PathBuf, sync::{mpsc::channel, Arc, Condvar, Mutex}};
|
||||
|
||||
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
||||
use utils::colors;
|
||||
@ -17,7 +17,7 @@ use crate::{
|
||||
extension::{self, parse_format},
|
||||
info,
|
||||
list::ListOptions,
|
||||
utils::{self, to_utf, EscapedPathDisplay, FileVisibilityPolicy},
|
||||
utils::{self, io::PrintMessage, to_utf, EscapedPathDisplay, FileVisibilityPolicy},
|
||||
warning, CliArgs, QuestionPolicy,
|
||||
};
|
||||
|
||||
@ -169,6 +169,27 @@ pub fn run(
|
||||
PathBuf::from(".")
|
||||
};
|
||||
|
||||
let (log_sender, log_receiver) = channel::<PrintMessage>();
|
||||
|
||||
let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let pair2 = Arc::clone(&pair);
|
||||
|
||||
// Log received messages until all senders are dropped
|
||||
rayon::spawn(move || {
|
||||
loop {
|
||||
let msg = log_receiver.recv();
|
||||
if let Ok(msg) = msg {
|
||||
println!("{}", msg.contents);
|
||||
} else {
|
||||
let (lock, cvar) = &*pair2;
|
||||
let mut flushed = lock.lock().unwrap();
|
||||
*flushed = true;
|
||||
cvar.notify_one();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
files
|
||||
.par_iter()
|
||||
.zip(formats)
|
||||
@ -182,8 +203,20 @@ pub fn run(
|
||||
output_file_path,
|
||||
question_policy,
|
||||
args.quiet,
|
||||
log_sender.clone(),
|
||||
)
|
||||
})?;
|
||||
|
||||
// Drop our sender clones so when all threads are done, no clones are left
|
||||
drop(log_sender);
|
||||
|
||||
// Prevent the main thread from exiting until the background thread handling the
|
||||
// logging has set `flushed` to true.
|
||||
let (lock, cvar) = &*pair;
|
||||
let mut flushed = lock.lock().unwrap();
|
||||
while !*flushed {
|
||||
flushed = cvar.wait(flushed).unwrap();
|
||||
}
|
||||
}
|
||||
Subcommand::List { archives: files, tree } => {
|
||||
let mut formats = vec![];
|
||||
|
5
src/utils/io.rs
Normal file
5
src/utils/io.rs
Normal file
@ -0,0 +1,5 @@
|
||||
#[derive(Debug)]
|
||||
pub struct PrintMessage {
|
||||
pub contents: String,
|
||||
pub accessible: bool,
|
||||
}
|
@ -7,6 +7,7 @@ pub mod colors;
|
||||
mod file_visibility;
|
||||
mod formatting;
|
||||
mod fs;
|
||||
pub mod io;
|
||||
mod question;
|
||||
|
||||
pub use file_visibility::FileVisibilityPolicy;
|
||||
|
@ -142,6 +142,7 @@ impl<'a> Confirmation<'a> {
|
||||
*colors::RESET
|
||||
);
|
||||
}
|
||||
let _stdout_lock = io::stdout().lock();
|
||||
io::stdout().flush()?;
|
||||
|
||||
let mut answer = String::new();
|
||||
|
Loading…
x
Reference in New Issue
Block a user