create Logger::info_accessible

This commit is contained in:
João Marcos P. Bezerra 2024-03-15 15:23:22 -03:00 committed by João Marcos
parent 1e56bb8f1f
commit bed8ea0276
12 changed files with 189 additions and 180 deletions

View File

@ -8,7 +8,7 @@ use crate::{error::Error, list::FileInArchive, utils::logger::Logger};
/// Unpacks the archive given by `archive_path` into the folder given by `output_folder`. /// Unpacks the archive given by `archive_path` into the folder given by `output_folder`.
/// Assumes that output_folder is empty /// Assumes that output_folder is empty
pub fn unpack_archive(archive_path: &Path, output_folder: &Path, quiet: bool, logger: Logger) -> crate::Result<usize> { pub fn unpack_archive(archive_path: &Path, output_folder: &Path, quiet: bool) -> 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 = Archive::new(archive_path).open_for_processing()?; let mut archive = Archive::new(archive_path).open_for_processing()?;
@ -18,10 +18,11 @@ pub fn unpack_archive(archive_path: &Path, output_folder: &Path, quiet: bool, lo
let entry = header.entry(); let entry = header.entry();
archive = if entry.is_file() { archive = if entry.is_file() {
if !quiet { if !quiet {
logger.info( logger.info(format!(
format!("{} extracted. ({})", entry.filename.display(), entry.unpacked_size), "{} extracted. ({})",
false, entry.filename.display(),
); entry.unpacked_size
));
} }
unpacked += 1; unpacked += 1;
header.extract_with_base(output_folder)? header.extract_with_base(output_folder)?

View File

@ -11,7 +11,7 @@ use same_file::Handle;
use crate::{ use crate::{
error::FinalError, error::FinalError,
utils::{self, cd_into_same_dir_as, logger::Logger, Bytes, EscapedPathDisplay, FileVisibilityPolicy}, utils::{self, cd_into_same_dir_as, Bytes, EscapedPathDisplay, FileVisibilityPolicy},
}; };
pub fn compress_sevenz<W>( pub fn compress_sevenz<W>(
@ -20,7 +20,6 @@ pub fn compress_sevenz<W>(
writer: W, writer: W,
file_visibility_policy: FileVisibilityPolicy, file_visibility_policy: FileVisibilityPolicy,
quiet: bool, quiet: bool,
logger: Logger,
) -> crate::Result<W> ) -> crate::Result<W>
where where
W: Write + Seek, W: Write + Seek,
@ -56,7 +55,7 @@ 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 {
logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path)), false); logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path)));
} }
let metadata = match path.metadata() { let metadata = match path.metadata() {
@ -93,7 +92,7 @@ where
Ok(bytes) Ok(bytes)
} }
pub fn decompress_sevenz<R>(reader: R, output_path: &Path, quiet: bool, logger: Logger) -> crate::Result<usize> pub fn decompress_sevenz<R>(reader: R, output_path: &Path, quiet: bool) -> crate::Result<usize>
where where
R: Read + Seek, R: Read + Seek,
{ {
@ -109,20 +108,22 @@ where
if entry.is_directory() { if entry.is_directory() {
if !quiet { if !quiet {
logger.info( logger.info(format!(
format!("File {} extracted to \"{}\"", entry.name(), file_path.display()), "File {} extracted to \"{}\"",
false, entry.name(),
); file_path.display()
));
} }
if !path.exists() { if !path.exists() {
fs::create_dir_all(path)?; fs::create_dir_all(path)?;
} }
} else { } else {
if !quiet { if !quiet {
logger.info( logger.info(format!(
format!("{:?} extracted. ({})", file_path.display(), Bytes::new(entry.size())), "{:?} extracted. ({})",
false, file_path.display(),
); Bytes::new(entry.size())
));
} }
if let Some(parent) = path.parent() { if let Some(parent) = path.parent() {

View File

@ -14,17 +14,12 @@ use same_file::Handle;
use crate::{ use crate::{
error::FinalError, error::FinalError,
list::FileInArchive, list::FileInArchive,
utils::{self, logger::Logger, Bytes, EscapedPathDisplay, FileVisibilityPolicy}, utils::{self, Bytes, EscapedPathDisplay, FileVisibilityPolicy},
}; };
/// 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( pub fn unpack_archive(reader: Box<dyn Read>, output_folder: &Path, quiet: bool) -> crate::Result<usize> {
reader: Box<dyn Read>,
output_folder: &Path,
quiet: bool,
logger: Logger,
) -> 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);
@ -39,14 +34,11 @@ pub fn unpack_archive(
// 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 {
logger.info( logger.info(format!(
format!( "{:?} extracted. ({})",
"{:?} extracted. ({})", utils::strip_cur_dir(&output_folder.join(file.path()?)),
utils::strip_cur_dir(&output_folder.join(file.path()?)), Bytes::new(file.size()),
Bytes::new(file.size()), ));
),
false,
);
files_unpacked += 1; files_unpacked += 1;
} }
@ -91,7 +83,6 @@ pub fn build_archive_from_paths<W>(
writer: W, writer: W,
file_visibility_policy: FileVisibilityPolicy, file_visibility_policy: FileVisibilityPolicy,
quiet: bool, quiet: bool,
logger: Logger,
) -> crate::Result<W> ) -> crate::Result<W>
where where
W: Write, W: Write,
@ -127,7 +118,7 @@ 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 {
logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path)), false); logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path)));
} }
if path.is_dir() { if path.is_dir() {

View File

@ -20,19 +20,14 @@ use crate::{
error::FinalError, error::FinalError,
list::FileInArchive, list::FileInArchive,
utils::{ utils::{
self, cd_into_same_dir_as, get_invalid_utf8_paths, logger::Logger, pretty_format_list_of_paths, strip_cur_dir, self, cd_into_same_dir_as, get_invalid_utf8_paths, pretty_format_list_of_paths, strip_cur_dir, Bytes,
Bytes, EscapedPathDisplay, FileVisibilityPolicy, EscapedPathDisplay, FileVisibilityPolicy,
}, },
}; };
/// 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>( pub fn unpack_archive<R>(mut archive: ZipArchive<R>, output_folder: &Path, quiet: bool) -> crate::Result<usize>
mut archive: ZipArchive<R>,
output_folder: &Path,
quiet: bool,
logger: Logger,
) -> crate::Result<usize>
where where
R: Read + Seek, R: Read + Seek,
{ {
@ -49,7 +44,7 @@ where
let file_path = output_folder.join(file_path); let file_path = output_folder.join(file_path);
display_zip_comment_if_exists(&file, logger.clone()); display_zip_comment_if_exists(&file);
match file.name().ends_with('/') { match file.name().ends_with('/') {
_is_dir @ true => { _is_dir @ true => {
@ -58,7 +53,7 @@ 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 {
logger.info(format!("File {} extracted to \"{}\"", idx, file_path.display()), false); logger.info(format!("File {} extracted to \"{}\"", idx, file_path.display()));
} }
fs::create_dir_all(&file_path)?; fs::create_dir_all(&file_path)?;
} }
@ -72,10 +67,11 @@ 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 {
logger.info( logger.info(format!(
format!("{:?} extracted. ({})", file_path.display(), Bytes::new(file.size())), "{:?} extracted. ({})",
false, file_path.display(),
); Bytes::new(file.size())
));
} }
let mut output_file = fs::File::create(file_path)?; let mut output_file = fs::File::create(file_path)?;
@ -138,7 +134,6 @@ pub fn build_archive_from_paths<W>(
writer: W, writer: W,
file_visibility_policy: FileVisibilityPolicy, file_visibility_policy: FileVisibilityPolicy,
quiet: bool, quiet: bool,
logger: Logger,
) -> crate::Result<W> ) -> crate::Result<W>
where where
W: Write + Seek, W: Write + Seek,
@ -192,7 +187,7 @@ 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 {
logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path)), false); logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path)));
} }
let metadata = match path.metadata() { let metadata = match path.metadata() {
@ -242,7 +237,7 @@ where
Ok(bytes) Ok(bytes)
} }
fn display_zip_comment_if_exists(file: &ZipFile, logger: Logger) { fn display_zip_comment_if_exists(file: &ZipFile) {
let comment = file.comment(); let comment = file.comment();
if !comment.is_empty() { if !comment.is_empty() {
// Zip file comments seem to be pretty rare, but if they are used, // Zip file comments seem to be pretty rare, but if they are used,
@ -255,7 +250,7 @@ fn display_zip_comment_if_exists(file: &ZipFile, logger: Logger) {
// the future, maybe asking the user if he wants to display the comment // the future, maybe asking the user if he wants to display the comment
// (informing him of its size) would be sensible for both normal and // (informing him of its size) would be sensible for both normal and
// accessibility mode.. // accessibility mode..
logger.info(format!("Found comment in {}: {}", file.name(), comment), true); info_accessible(format!("Found comment in {}: {}", file.name(), comment));
} }
} }

View File

@ -11,9 +11,7 @@ use std::{
use crate::{ use crate::{
error::FinalError, error::FinalError,
extension::{build_archive_file_suggestion, Extension, PRETTY_SUPPORTED_ALIASES, PRETTY_SUPPORTED_EXTENSIONS}, extension::{build_archive_file_suggestion, Extension, PRETTY_SUPPORTED_ALIASES, PRETTY_SUPPORTED_EXTENSIONS},
utils::{ utils::{pretty_format_list_of_paths, try_infer_extension, user_wants_to_continue, EscapedPathDisplay},
logger::Logger, pretty_format_list_of_paths, try_infer_extension, user_wants_to_continue, EscapedPathDisplay,
},
QuestionAction, QuestionPolicy, Result, QuestionAction, QuestionPolicy, Result,
}; };
@ -27,7 +25,6 @@ pub fn check_mime_type(
path: &Path, path: &Path,
formats: &mut Vec<Extension>, formats: &mut Vec<Extension>,
question_policy: QuestionPolicy, question_policy: QuestionPolicy,
logger: Logger,
) -> Result<ControlFlow<()>> { ) -> Result<ControlFlow<()>> {
if formats.is_empty() { if formats.is_empty() {
// File with no extension // File with no extension
@ -35,10 +32,11 @@ pub fn check_mime_type(
if let Some(detected_format) = try_infer_extension(path) { if let Some(detected_format) = try_infer_extension(path) {
// Inferring the file extension can have unpredicted consequences (e.g. the user just // Inferring the file extension can have unpredicted consequences (e.g. the user just
// mistyped, ...) which we should always inform the user about. // mistyped, ...) which we should always inform the user about.
logger.info( info_accessible(format!(
format!("Detected file: `{}` extension as `{}`", path.display(), detected_format), "Detected file: `{}` extension as `{}`",
true, path.display(),
); detected_format
));
if user_wants_to_continue(path, question_policy, QuestionAction::Decompression)? { if user_wants_to_continue(path, question_policy, QuestionAction::Decompression)? {
formats.push(detected_format); formats.push(detected_format);
@ -67,13 +65,10 @@ pub fn check_mime_type(
} else { } else {
// NOTE: If this actually produces no false positives, we can upgrade it in the future // NOTE: If this actually produces no false positives, we can upgrade it in the future
// to a warning and ask the user if he wants to continue decompressing. // to a warning and ask the user if he wants to continue decompressing.
logger.info( info_accessible(format!(
format!( "Failed to confirm the format of `{}` by sniffing the contents, file might be misnamed",
"Failed to confirm the format of `{}` by sniffing the contents, file might be misnamed", path.display()
path.display() ));
),
true,
);
} }
Ok(ControlFlow::Continue(())) Ok(ControlFlow::Continue(()))
} }

View File

@ -10,7 +10,7 @@ use crate::{
archive, archive,
commands::warn_user_about_loading_zip_in_memory, commands::warn_user_about_loading_zip_in_memory,
extension::{split_first_compression_format, CompressionFormat::*, Extension}, extension::{split_first_compression_format, CompressionFormat::*, Extension},
utils::{logger::Logger, user_wants_to_continue, FileVisibilityPolicy}, utils::{user_wants_to_continue, FileVisibilityPolicy},
QuestionAction, QuestionPolicy, BUFFER_CAPACITY, QuestionAction, QuestionPolicy, BUFFER_CAPACITY,
}; };
@ -34,7 +34,6 @@ pub fn compress_files(
question_policy: QuestionPolicy, question_policy: QuestionPolicy,
file_visibility_policy: FileVisibilityPolicy, file_visibility_policy: FileVisibilityPolicy,
level: Option<i16>, level: Option<i16>,
logger: Logger,
) -> crate::Result<bool> { ) -> crate::Result<bool> {
// If the input files contain a directory, then the total size will be underestimated // If the input files contain a directory, then the total size will be underestimated
let file_writer = BufWriter::with_capacity(BUFFER_CAPACITY, output_file); let file_writer = BufWriter::with_capacity(BUFFER_CAPACITY, output_file);
@ -100,14 +99,7 @@ pub fn compress_files(
io::copy(&mut reader, &mut writer)?; io::copy(&mut reader, &mut writer)?;
} }
Tar => { Tar => {
archive::tar::build_archive_from_paths( archive::tar::build_archive_from_paths(&files, output_path, &mut writer, file_visibility_policy, quiet)?;
&files,
output_path,
&mut writer,
file_visibility_policy,
quiet,
logger.clone(),
)?;
writer.flush()?; writer.flush()?;
} }
Zip => { Zip => {
@ -127,7 +119,6 @@ pub fn compress_files(
&mut vec_buffer, &mut vec_buffer,
file_visibility_policy, file_visibility_policy,
quiet, quiet,
logger.clone(),
)?; )?;
vec_buffer.rewind()?; vec_buffer.rewind()?;
io::copy(&mut vec_buffer, &mut writer)?; io::copy(&mut vec_buffer, &mut writer)?;
@ -149,14 +140,7 @@ pub fn compress_files(
} }
let mut vec_buffer = Cursor::new(vec![]); let mut vec_buffer = Cursor::new(vec![]);
archive::sevenz::compress_sevenz( archive::sevenz::compress_sevenz(&files, output_path, &mut vec_buffer, file_visibility_policy, quiet)?;
&files,
output_path,
&mut vec_buffer,
file_visibility_policy,
quiet,
logger.clone(),
)?;
vec_buffer.rewind()?; vec_buffer.rewind()?;
io::copy(&mut vec_buffer, &mut writer)?; io::copy(&mut vec_buffer, &mut writer)?;
} }

View File

@ -13,7 +13,7 @@ use crate::{
CompressionFormat::{self, *}, CompressionFormat::{self, *},
Extension, Extension,
}, },
utils::{self, logger::Logger, nice_directory_display, user_wants_to_continue}, utils::{self, nice_directory_display, user_wants_to_continue},
QuestionAction, QuestionPolicy, BUFFER_CAPACITY, QuestionAction, QuestionPolicy, BUFFER_CAPACITY,
}; };
@ -30,7 +30,6 @@ pub fn decompress_file(
output_file_path: PathBuf, output_file_path: PathBuf,
question_policy: QuestionPolicy, question_policy: QuestionPolicy,
quiet: bool, quiet: bool,
logger: Logger,
) -> crate::Result<()> { ) -> crate::Result<()> {
assert!(output_dir.exists()); assert!(output_dir.exists());
let reader = fs::File::open(input_file_path)?; let reader = fs::File::open(input_file_path)?;
@ -49,11 +48,10 @@ 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, logger.clone()), |output_dir| crate::archive::zip::unpack_archive(zip_archive, output_dir, quiet),
output_dir, output_dir,
&output_file_path, &output_file_path,
question_policy, question_policy,
logger.clone(),
)? { )? {
files files
} else { } else {
@ -64,14 +62,11 @@ 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
logger.info( info_accessible(format!(
format!( "Successfully decompressed archive in {} ({} files).",
"Successfully decompressed archive in {} ({} files).", nice_directory_display(output_dir),
nice_directory_display(output_dir), files_unpacked
files_unpacked ));
),
true,
);
return Ok(()); return Ok(());
} }
@ -115,11 +110,10 @@ pub fn decompress_file(
} }
Tar => { Tar => {
if let ControlFlow::Continue(files) = smart_unpack( if let ControlFlow::Continue(files) = smart_unpack(
|output_dir| crate::archive::tar::unpack_archive(reader, output_dir, quiet, logger.clone()), |output_dir| crate::archive::tar::unpack_archive(reader, output_dir, quiet),
output_dir, output_dir,
&output_file_path, &output_file_path,
question_policy, question_policy,
logger.clone(),
)? { )? {
files files
} else { } else {
@ -140,11 +134,10 @@ 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, logger.clone()), |output_dir| crate::archive::zip::unpack_archive(zip_archive, output_dir, quiet),
output_dir, output_dir,
&output_file_path, &output_file_path,
question_policy, question_policy,
logger.clone(),
)? { )? {
files files
} else { } else {
@ -162,18 +155,12 @@ pub fn decompress_file(
crate::archive::rar::unpack_archive(temp_file.path(), output_dir, quiet, logger_clone) crate::archive::rar::unpack_archive(temp_file.path(), output_dir, quiet, logger_clone)
}) })
} else { } else {
Box::new(|output_dir| { Box::new(|output_dir| crate::archive::rar::unpack_archive(input_file_path, output_dir, quiet))
crate::archive::rar::unpack_archive(input_file_path, output_dir, quiet, logger.clone())
})
}; };
if let ControlFlow::Continue(files) = smart_unpack( if let ControlFlow::Continue(files) =
unpack_fn, smart_unpack(unpack_fn, output_dir, &output_file_path, question_policy)?
output_dir, {
&output_file_path,
question_policy,
logger.clone(),
)? {
files files
} else { } else {
return Ok(()); return Ok(());
@ -196,13 +183,10 @@ pub fn decompress_file(
io::copy(&mut reader, &mut vec)?; io::copy(&mut reader, &mut vec)?;
if let ControlFlow::Continue(files) = smart_unpack( if let ControlFlow::Continue(files) = smart_unpack(
|output_dir| { |output_dir| crate::archive::sevenz::decompress_sevenz(io::Cursor::new(vec), output_dir, quiet),
crate::archive::sevenz::decompress_sevenz(io::Cursor::new(vec), output_dir, quiet, logger.clone())
},
output_dir, output_dir,
&output_file_path, &output_file_path,
question_policy, question_policy,
logger.clone(),
)? { )? {
files files
} else { } else {
@ -215,14 +199,11 @@ 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
logger.info( info_accessible(format!(
format!( "Successfully decompressed archive in {}.",
"Successfully decompressed archive in {}.", nice_directory_display(output_dir)
nice_directory_display(output_dir) ));
), info_accessible(format!("Files unpacked: {}", files_unpacked));
true,
);
logger.info(format!("Files unpacked: {}", files_unpacked), true);
Ok(()) Ok(())
} }
@ -237,19 +218,15 @@ fn smart_unpack(
output_dir: &Path, output_dir: &Path,
output_file_path: &Path, output_file_path: &Path,
question_policy: QuestionPolicy, question_policy: QuestionPolicy,
logger: Logger,
) -> crate::Result<ControlFlow<(), usize>> { ) -> crate::Result<ControlFlow<(), usize>> {
assert!(output_dir.exists()); assert!(output_dir.exists());
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();
logger.info( info_accessible(format!(
format!( "Created temporary directory {} to hold decompressed elements.",
"Created temporary directory {} to hold decompressed elements.", nice_directory_display(temp_dir_path)
nice_directory_display(temp_dir_path) ));
),
true,
);
let files = unpack_fn(temp_dir_path)?; let files = unpack_fn(temp_dir_path)?;
@ -268,14 +245,11 @@ fn smart_unpack(
} }
fs::rename(&file_path, &correct_path)?; fs::rename(&file_path, &correct_path)?;
logger.info( info_accessible(format!(
format!( "Successfully moved {} to {}.",
"Successfully moved {} to {}.", nice_directory_display(&file_path),
nice_directory_display(&file_path), nice_directory_display(&correct_path)
nice_directory_display(&correct_path) ));
),
true,
);
} 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
@ -284,14 +258,11 @@ 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)?;
logger.info( info_accessible(format!(
format!( "Successfully moved {} to {}.",
"Successfully moved {} to {}.", nice_directory_display(temp_dir_path),
nice_directory_display(temp_dir_path), nice_directory_display(output_file_path)
nice_directory_display(output_file_path) ));
),
true,
);
} }
Ok(ControlFlow::Continue(files)) Ok(ControlFlow::Continue(files))

View File

@ -9,7 +9,7 @@ use crate::{
commands::warn_user_about_loading_zip_in_memory, commands::warn_user_about_loading_zip_in_memory,
extension::CompressionFormat::{self, *}, extension::CompressionFormat::{self, *},
list::{self, FileInArchive, ListOptions}, list::{self, FileInArchive, ListOptions},
utils::{logger::Logger, user_wants_to_continue}, utils::user_wants_to_continue,
QuestionAction, QuestionPolicy, BUFFER_CAPACITY, QuestionAction, QuestionPolicy, BUFFER_CAPACITY,
}; };
@ -20,7 +20,6 @@ pub fn list_archive_contents(
formats: Vec<CompressionFormat>, formats: Vec<CompressionFormat>,
list_options: ListOptions, list_options: ListOptions,
question_policy: QuestionPolicy, question_policy: QuestionPolicy,
logger: Logger,
) -> crate::Result<()> { ) -> crate::Result<()> {
let reader = fs::File::open(archive_path)?; let reader = fs::File::open(archive_path)?;

View File

@ -7,7 +7,7 @@ mod list;
use std::{ use std::{
ops::ControlFlow, ops::ControlFlow,
path::PathBuf, path::PathBuf,
sync::{mpsc::channel, Arc, Condvar, Mutex}, sync::{mpsc, Arc, Condvar, Mutex, OnceLock},
}; };
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
@ -22,7 +22,7 @@ use crate::{
list::ListOptions, list::ListOptions,
utils::{ utils::{
self, self,
logger::{map_message, Logger, PrintMessage}, logger::{map_message, setup_channel, PrintMessage},
to_utf, EscapedPathDisplay, FileVisibilityPolicy, to_utf, EscapedPathDisplay, FileVisibilityPolicy,
}, },
CliArgs, QuestionPolicy, CliArgs, QuestionPolicy,
@ -57,8 +57,7 @@ pub fn run(
question_policy: QuestionPolicy, question_policy: QuestionPolicy,
file_visibility_policy: FileVisibilityPolicy, file_visibility_policy: FileVisibilityPolicy,
) -> crate::Result<()> { ) -> crate::Result<()> {
let (log_sender, log_receiver) = channel::<PrintMessage>(); let log_receiver = setup_channel();
let logger = Logger::new(log_sender);
let pair = Arc::new((Mutex::new(false), Condvar::new())); let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = Arc::clone(&pair); let pair2 = Arc::clone(&pair);
@ -119,7 +118,7 @@ pub fn run(
let parsed_formats = parse_format(&formats)?; let parsed_formats = parse_format(&formats)?;
(Some(formats), parsed_formats) (Some(formats), parsed_formats)
} }
None => (None, extension::extensions_from_path(&output_path, logger.clone())), None => (None, extension::extensions_from_path(&output_path)),
}; };
check::check_invalid_compression_with_non_archive_format( check::check_invalid_compression_with_non_archive_format(
@ -152,7 +151,6 @@ pub fn run(
question_policy, question_policy,
file_visibility_policy, file_visibility_policy,
level, level,
logger.clone(),
); );
if let Ok(true) = compress_result { if let Ok(true) = compress_result {
@ -160,7 +158,7 @@ pub fn run(
// 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
logger.info(format!("Successfully compressed '{}'.", to_utf(&output_path)), true); info_accessible(format!("Successfully compressed '{}'.", to_utf(&output_path)));
} else { } else {
// If Ok(false) or Err() occurred, delete incomplete file at `output_path` // If Ok(false) or Err() occurred, delete incomplete file at `output_path`
// //
@ -198,12 +196,9 @@ pub fn run(
} }
} else { } else {
for path in files.iter() { for path in files.iter() {
let (pathbase, mut file_formats) = let (pathbase, mut file_formats) = extension::separate_known_extensions_from_name(path);
extension::separate_known_extensions_from_name(path, logger.clone());
if let ControlFlow::Break(_) = if let ControlFlow::Break(_) = check::check_mime_type(path, &mut file_formats, question_policy)? {
check::check_mime_type(path, &mut file_formats, question_policy, logger.clone())?
{
return Ok(()); return Ok(());
} }
@ -217,7 +212,7 @@ pub fn run(
// The directory that will contain the output files // The directory that will contain the output files
// We default to the current directory if the user didn't specify an output directory with --dir // We default to the current directory if the user didn't specify an output directory with --dir
let output_dir = if let Some(dir) = output_dir { let output_dir = if let Some(dir) = output_dir {
utils::create_dir_if_non_existent(&dir, logger.clone())?; utils::create_dir_if_non_existent(&dir)?;
dir dir
} else { } else {
PathBuf::from(".") PathBuf::from(".")
@ -236,7 +231,6 @@ pub fn run(
output_file_path, output_file_path,
question_policy, question_policy,
args.quiet, args.quiet,
logger.clone(),
) )
})?; })?;
} }
@ -250,11 +244,9 @@ pub fn run(
} }
} else { } else {
for path in files.iter() { for path in files.iter() {
let mut file_formats = extension::extensions_from_path(path, logger.clone()); let mut file_formats = extension::extensions_from_path(path);
if let ControlFlow::Break(_) = if let ControlFlow::Break(_) = check::check_mime_type(path, &mut file_formats, question_policy)? {
check::check_mime_type(path, &mut file_formats, question_policy, logger.clone())?
{
return Ok(()); return Ok(());
} }
@ -272,7 +264,7 @@ pub fn run(
println!(); println!();
} }
let formats = extension::flatten_compression_formats(&formats); let formats = extension::flatten_compression_formats(&formats);
list_archive_contents(archive_path, formats, list_options, question_policy, logger.clone())?; list_archive_contents(archive_path, formats, list_options, question_policy)?;
} }
} }
} }

View File

@ -169,7 +169,7 @@ pub fn parse_format(fmt: &OsStr) -> crate::Result<Vec<Extension>> {
/// Extracts extensions from a path. /// Extracts extensions from a path.
/// ///
/// Returns both the remaining path and the list of extension objects /// Returns both the remaining path and the list of extension objects
pub fn separate_known_extensions_from_name(path: &Path, logger: Logger) -> (&Path, Vec<Extension>) { pub fn separate_known_extensions_from_name(path: &Path) -> (&Path, Vec<Extension>) {
let mut extensions = vec![]; let mut extensions = vec![];
let Some(mut name) = path.file_name().and_then(<[u8] as ByteSlice>::from_os_str) else { let Some(mut name) = path.file_name().and_then(<[u8] as ByteSlice>::from_os_str) else {
@ -194,8 +194,8 @@ pub fn separate_known_extensions_from_name(path: &Path, logger: Logger) -> (&Pat
} }
/// Extracts extensions from a path, return only the list of extension objects /// Extracts extensions from a path, return only the list of extension objects
pub fn extensions_from_path(path: &Path, logger: Logger) -> Vec<Extension> { pub fn extensions_from_path(path: &Path) -> Vec<Extension> {
let (_, extensions) = separate_known_extensions_from_name(path, logger); let (_, extensions) = separate_known_extensions_from_name(path);
extensions extensions
} }
@ -260,7 +260,7 @@ mod tests {
let path = Path::new("bolovo.tar.gz"); let path = Path::new("bolovo.tar.gz");
let extensions: Vec<Extension> = extensions_from_path(path, logger); let extensions: Vec<Extension> = extensions_from_path(path);
let formats: Vec<CompressionFormat> = flatten_compression_formats(&extensions); let formats: Vec<CompressionFormat> = flatten_compression_formats(&extensions);
assert_eq!(formats, vec![Tar, Gzip]); assert_eq!(formats, vec![Tar, Gzip]);

View File

@ -8,7 +8,7 @@ use std::{
use fs_err as fs; use fs_err as fs;
use super::{logger::Logger, user_wants_to_overwrite}; use super::user_wants_to_overwrite;
use crate::{extension::Extension, utils::EscapedPathDisplay, QuestionPolicy}; use crate::{extension::Extension, utils::EscapedPathDisplay, QuestionPolicy};
/// Remove `path` asking the user to overwrite if necessary. /// Remove `path` asking the user to overwrite if necessary.
@ -36,12 +36,12 @@ pub fn remove_file_or_dir(path: &Path) -> crate::Result<()> {
} }
/// Creates a directory at the path, if there is nothing there. /// Creates a directory at the path, if there is nothing there.
pub fn create_dir_if_non_existent(path: &Path, logger: Logger) -> crate::Result<()> { pub fn create_dir_if_non_existent(path: &Path) -> crate::Result<()> {
if !path.exists() { if !path.exists() {
fs::create_dir_all(path)?; fs::create_dir_all(path)?;
// creating a directory is an important change to the file system we // creating a directory is an important change to the file system we
// should always inform the user about // should always inform the user about
logger.info(format!("Directory {} created.", EscapedPathDisplay::new(path)), true); info_accessible(format!("Directory {} created.", EscapedPathDisplay::new(path)));
} }
Ok(()) Ok(())
} }

View File

@ -1,10 +1,26 @@
use std::sync::mpsc::Sender; use std::sync::{
mpsc::{channel, Receiver, Sender},
OnceLock,
};
use super::colors::{ORANGE, RESET, YELLOW}; use super::colors::{ORANGE, RESET, YELLOW};
use crate::accessible::is_running_in_accessible_mode; use crate::accessible::is_running_in_accessible_mode;
static SENDER: OnceLock<Sender<PrintMessage>> = OnceLock::new();
pub fn setup_channel() -> Receiver<PrintMessage> {
let (tx, rx) = channel();
SENDER.set(tx).expect("`setup_channel` should only be called once");
rx
}
#[track_caller]
fn get_sender() -> &'static Sender<PrintMessage> {
SENDER.get().expect("No sender, you need to call `setup_channel` first")
}
/// Message object used for sending logs from worker threads to a logging thread via channels. /// Message object used for sending logs from worker threads to a logging thread via channels.
/// See <https://github.com/ouch-org/ouch/issues/632> /// See <https://github.com/ouch-org/ouch/issues/643>
#[derive(Debug)] #[derive(Debug)]
pub struct PrintMessage { pub struct PrintMessage {
contents: String, contents: String,
@ -37,6 +53,48 @@ pub fn map_message(msg: &PrintMessage) -> Option<String> {
} }
} }
/// An `[INFO]` log to be displayed if we're not running accessibility mode.
///
/// Same as `.info_accessible()`, but only displayed if accessibility mode
/// is turned off, which is detected by the function
/// `is_running_in_accessible_mode`.
///
/// Read more about accessibility mode in `accessible.rs`.
pub fn info(contents: String) {
info_with_accessibility(contents, false);
}
/// An `[INFO]` log to be displayed.
///
/// Same as `.info()`, but also displays if `is_running_in_accessible_mode`
/// returns `true`.
///
/// Read more about accessibility mode in `accessible.rs`.
pub fn info_accessible(contents: String) {
info_with_accessibility(contents, true);
}
fn info_with_accessibility(contents: String, accessible: bool) {
get_sender()
.send(PrintMessage {
contents,
accessible,
level: MessageLevel::Info,
})
.unwrap();
}
pub fn warning(contents: String) {
get_sender()
.send(PrintMessage {
contents,
// Warnings are important and unlikely to flood, so they should be displayed
accessible: true,
level: MessageLevel::Warning,
})
.unwrap();
}
#[derive(Clone)] #[derive(Clone)]
pub struct Logger { pub struct Logger {
log_sender: Sender<PrintMessage>, log_sender: Sender<PrintMessage>,
@ -47,7 +105,28 @@ impl Logger {
Self { log_sender } Self { log_sender }
} }
pub fn info(&self, contents: String, accessible: bool) { /// An `[INFO]` log to be displayed if we're not running accessibility mode.
///
/// Same as `.info_accessible()`, but only displayed if accessibility mode
/// is turned off, which is detected by the function
/// `is_running_in_accessible_mode`.
///
/// Read more about accessibility mode in `accessible.rs`.
pub fn info(&self, contents: String) {
self.info_with_accessibility(contents, false);
}
/// An `[INFO]` log to be displayed.
///
/// Same as `.info()`, but also displays if `is_running_in_accessible_mode`
/// returns `true`.
///
/// Read more about accessibility mode in `accessible.rs`.
pub fn info_accessible(&self, contents: String) {
self.info_with_accessibility(contents, true);
}
fn info_with_accessibility(&self, contents: String, accessible: bool) {
self.log_sender self.log_sender
.send(PrintMessage { .send(PrintMessage {
contents, contents,
@ -61,7 +140,8 @@ impl Logger {
self.log_sender self.log_sender
.send(PrintMessage { .send(PrintMessage {
contents, contents,
accessible: true, // does not matter // Warnings are important and unlikely to flood, so they should be displayed
accessible: true,
level: MessageLevel::Warning, level: MessageLevel::Warning,
}) })
.unwrap(); .unwrap();