This commit is contained in:
Antonios Barotsis 2024-03-06 22:33:24 +01:00
parent 5b0e0b6991
commit 3a3f38c93e
6 changed files with 107 additions and 44 deletions

View File

@ -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;
}

View File

@ -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))

View File

@ -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
View File

@ -0,0 +1,5 @@
#[derive(Debug)]
pub struct PrintMessage {
pub contents: String,
pub accessible: bool,
}

View File

@ -7,6 +7,7 @@ pub mod colors;
mod file_visibility;
mod formatting;
mod fs;
pub mod io;
mod question;
pub use file_visibility::FileVisibilityPolicy;

View File

@ -142,6 +142,7 @@ impl<'a> Confirmation<'a> {
*colors::RESET
);
}
let _stdout_lock = io::stdout().lock();
io::stdout().flush()?;
let mut answer = String::new();