Added buffering

This commit is contained in:
Antonios Barotsis 2024-03-07 21:29:15 +01:00 committed by João Marcos
parent c2873f77d3
commit e989db7a3a
5 changed files with 129 additions and 81 deletions

View File

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

View File

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

View File

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

View File

@ -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![];

View File

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