From bed8ea0276634234ccd6e3a4a86862dd44db5723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Marcos=20P=2E=20Bezerra?= Date: Fri, 15 Mar 2024 15:23:22 -0300 Subject: [PATCH] create `Logger::info_accessible` --- src/archive/rar.rs | 11 +++-- src/archive/sevenz.rs | 25 +++++----- src/archive/tar.rs | 25 ++++------ src/archive/zip.rs | 31 ++++++------- src/check.rs | 25 ++++------ src/commands/compress.rs | 22 ++------- src/commands/decompress.rs | 95 +++++++++++++------------------------- src/commands/list.rs | 3 +- src/commands/mod.rs | 30 +++++------- src/extension.rs | 8 ++-- src/utils/fs.rs | 6 +-- src/utils/logger.rs | 88 +++++++++++++++++++++++++++++++++-- 12 files changed, 189 insertions(+), 180 deletions(-) diff --git a/src/archive/rar.rs b/src/archive/rar.rs index 9e59357..072de5e 100644 --- a/src/archive/rar.rs +++ b/src/archive/rar.rs @@ -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`. /// Assumes that output_folder is empty -pub fn unpack_archive(archive_path: &Path, output_folder: &Path, quiet: bool, logger: Logger) -> crate::Result { +pub fn unpack_archive(archive_path: &Path, output_folder: &Path, quiet: bool) -> crate::Result { assert!(output_folder.read_dir().expect("dir exists").count() == 0); 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(); archive = if entry.is_file() { if !quiet { - logger.info( - format!("{} extracted. ({})", entry.filename.display(), entry.unpacked_size), - false, - ); + logger.info(format!( + "{} extracted. ({})", + entry.filename.display(), + entry.unpacked_size + )); } unpacked += 1; header.extract_with_base(output_folder)? diff --git a/src/archive/sevenz.rs b/src/archive/sevenz.rs index fda31ec..5272203 100644 --- a/src/archive/sevenz.rs +++ b/src/archive/sevenz.rs @@ -11,7 +11,7 @@ use same_file::Handle; use crate::{ 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( @@ -20,7 +20,6 @@ pub fn compress_sevenz( writer: W, file_visibility_policy: FileVisibilityPolicy, quiet: bool, - logger: Logger, ) -> crate::Result where W: Write + Seek, @@ -56,7 +55,7 @@ where // spoken text for users using screen readers, braille displays // and so on if !quiet { - logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path)), false); + logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path))); } let metadata = match path.metadata() { @@ -93,7 +92,7 @@ where Ok(bytes) } -pub fn decompress_sevenz(reader: R, output_path: &Path, quiet: bool, logger: Logger) -> crate::Result +pub fn decompress_sevenz(reader: R, output_path: &Path, quiet: bool) -> crate::Result where R: Read + Seek, { @@ -109,20 +108,22 @@ where if entry.is_directory() { if !quiet { - logger.info( - format!("File {} extracted to \"{}\"", entry.name(), file_path.display()), - false, - ); + logger.info(format!( + "File {} extracted to \"{}\"", + entry.name(), + file_path.display() + )); } if !path.exists() { fs::create_dir_all(path)?; } } else { if !quiet { - logger.info( - format!("{:?} extracted. ({})", file_path.display(), Bytes::new(entry.size())), - false, - ); + logger.info(format!( + "{:?} extracted. ({})", + file_path.display(), + Bytes::new(entry.size()) + )); } if let Some(parent) = path.parent() { diff --git a/src/archive/tar.rs b/src/archive/tar.rs index 77e7e61..c75f9d2 100644 --- a/src/archive/tar.rs +++ b/src/archive/tar.rs @@ -14,17 +14,12 @@ use same_file::Handle; use crate::{ error::FinalError, 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`. /// Assumes that output_folder is empty -pub fn unpack_archive( - reader: Box, - output_folder: &Path, - quiet: bool, - logger: Logger, -) -> crate::Result { +pub fn unpack_archive(reader: Box, output_folder: &Path, quiet: bool) -> crate::Result { assert!(output_folder.read_dir().expect("dir exists").count() == 0); let mut archive = tar::Archive::new(reader); @@ -39,14 +34,11 @@ pub fn unpack_archive( // spoken text for users using screen readers, braille displays // and so on if !quiet { - logger.info( - format!( - "{:?} extracted. ({})", - utils::strip_cur_dir(&output_folder.join(file.path()?)), - Bytes::new(file.size()), - ), - false, - ); + logger.info(format!( + "{:?} extracted. ({})", + utils::strip_cur_dir(&output_folder.join(file.path()?)), + Bytes::new(file.size()), + )); files_unpacked += 1; } @@ -91,7 +83,6 @@ pub fn build_archive_from_paths( writer: W, file_visibility_policy: FileVisibilityPolicy, quiet: bool, - logger: Logger, ) -> crate::Result where W: Write, @@ -127,7 +118,7 @@ where // spoken text for users using screen readers, braille displays // and so on if !quiet { - logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path)), false); + logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path))); } if path.is_dir() { diff --git a/src/archive/zip.rs b/src/archive/zip.rs index e1dbf5c..23a3295 100644 --- a/src/archive/zip.rs +++ b/src/archive/zip.rs @@ -20,19 +20,14 @@ use crate::{ error::FinalError, list::FileInArchive, utils::{ - self, cd_into_same_dir_as, get_invalid_utf8_paths, logger::Logger, pretty_format_list_of_paths, strip_cur_dir, - Bytes, EscapedPathDisplay, FileVisibilityPolicy, + self, cd_into_same_dir_as, get_invalid_utf8_paths, pretty_format_list_of_paths, strip_cur_dir, Bytes, + EscapedPathDisplay, FileVisibilityPolicy, }, }; /// Unpacks the archive given by `archive` into the folder given by `output_folder`. /// Assumes that output_folder is empty -pub fn unpack_archive( - mut archive: ZipArchive, - output_folder: &Path, - quiet: bool, - logger: Logger, -) -> crate::Result +pub fn unpack_archive(mut archive: ZipArchive, output_folder: &Path, quiet: bool) -> crate::Result where R: Read + Seek, { @@ -49,7 +44,7 @@ where 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('/') { _is_dir @ true => { @@ -58,7 +53,7 @@ where // spoken text for users using screen readers, braille displays // and so on 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)?; } @@ -72,10 +67,11 @@ where // same reason is in _is_dir: long, often not needed text if !quiet { - logger.info( - format!("{:?} extracted. ({})", file_path.display(), Bytes::new(file.size())), - false, - ); + logger.info(format!( + "{:?} extracted. ({})", + file_path.display(), + Bytes::new(file.size()) + )); } let mut output_file = fs::File::create(file_path)?; @@ -138,7 +134,6 @@ pub fn build_archive_from_paths( writer: W, file_visibility_policy: FileVisibilityPolicy, quiet: bool, - logger: Logger, ) -> crate::Result where W: Write + Seek, @@ -192,7 +187,7 @@ where // spoken text for users using screen readers, braille displays // and so on if !quiet { - logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path)), false); + logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path))); } let metadata = match path.metadata() { @@ -242,7 +237,7 @@ where Ok(bytes) } -fn display_zip_comment_if_exists(file: &ZipFile, logger: Logger) { +fn display_zip_comment_if_exists(file: &ZipFile) { let comment = file.comment(); if !comment.is_empty() { // 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 // (informing him of its size) would be sensible for both normal and // accessibility mode.. - logger.info(format!("Found comment in {}: {}", file.name(), comment), true); + info_accessible(format!("Found comment in {}: {}", file.name(), comment)); } } diff --git a/src/check.rs b/src/check.rs index 564b6e1..c46cc74 100644 --- a/src/check.rs +++ b/src/check.rs @@ -11,9 +11,7 @@ use std::{ use crate::{ error::FinalError, extension::{build_archive_file_suggestion, Extension, PRETTY_SUPPORTED_ALIASES, PRETTY_SUPPORTED_EXTENSIONS}, - utils::{ - logger::Logger, pretty_format_list_of_paths, try_infer_extension, user_wants_to_continue, EscapedPathDisplay, - }, + utils::{pretty_format_list_of_paths, try_infer_extension, user_wants_to_continue, EscapedPathDisplay}, QuestionAction, QuestionPolicy, Result, }; @@ -27,7 +25,6 @@ pub fn check_mime_type( path: &Path, formats: &mut Vec, question_policy: QuestionPolicy, - logger: Logger, ) -> Result> { if formats.is_empty() { // File with no extension @@ -35,10 +32,11 @@ pub fn check_mime_type( if let Some(detected_format) = try_infer_extension(path) { // Inferring the file extension can have unpredicted consequences (e.g. the user just // mistyped, ...) which we should always inform the user about. - logger.info( - format!("Detected file: `{}` extension as `{}`", path.display(), detected_format), - true, - ); + info_accessible(format!( + "Detected file: `{}` extension as `{}`", + path.display(), + detected_format + )); if user_wants_to_continue(path, question_policy, QuestionAction::Decompression)? { formats.push(detected_format); @@ -67,13 +65,10 @@ pub fn check_mime_type( } else { // 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. - logger.info( - format!( - "Failed to confirm the format of `{}` by sniffing the contents, file might be misnamed", - path.display() - ), - true, - ); + info_accessible(format!( + "Failed to confirm the format of `{}` by sniffing the contents, file might be misnamed", + path.display() + )); } Ok(ControlFlow::Continue(())) } diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 273d0d4..ea7e1cc 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -10,7 +10,7 @@ use crate::{ archive, commands::warn_user_about_loading_zip_in_memory, 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, }; @@ -34,7 +34,6 @@ pub fn compress_files( question_policy: QuestionPolicy, file_visibility_policy: FileVisibilityPolicy, level: Option, - logger: Logger, ) -> crate::Result { // If the input files contain a directory, then the total size will be underestimated let file_writer = BufWriter::with_capacity(BUFFER_CAPACITY, output_file); @@ -100,14 +99,7 @@ pub fn compress_files( io::copy(&mut reader, &mut writer)?; } Tar => { - archive::tar::build_archive_from_paths( - &files, - output_path, - &mut writer, - file_visibility_policy, - quiet, - logger.clone(), - )?; + archive::tar::build_archive_from_paths(&files, output_path, &mut writer, file_visibility_policy, quiet)?; writer.flush()?; } Zip => { @@ -127,7 +119,6 @@ pub fn compress_files( &mut vec_buffer, file_visibility_policy, quiet, - logger.clone(), )?; vec_buffer.rewind()?; io::copy(&mut vec_buffer, &mut writer)?; @@ -149,14 +140,7 @@ pub fn compress_files( } let mut vec_buffer = Cursor::new(vec![]); - archive::sevenz::compress_sevenz( - &files, - output_path, - &mut vec_buffer, - file_visibility_policy, - quiet, - logger.clone(), - )?; + archive::sevenz::compress_sevenz(&files, output_path, &mut vec_buffer, file_visibility_policy, quiet)?; vec_buffer.rewind()?; io::copy(&mut vec_buffer, &mut writer)?; } diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index 5c4dec9..00390e8 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -13,7 +13,7 @@ use crate::{ CompressionFormat::{self, *}, 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, }; @@ -30,7 +30,6 @@ pub fn decompress_file( output_file_path: PathBuf, question_policy: QuestionPolicy, quiet: bool, - logger: Logger, ) -> crate::Result<()> { assert!(output_dir.exists()); let reader = fs::File::open(input_file_path)?; @@ -49,11 +48,10 @@ pub fn decompress_file( { let zip_archive = zip::ZipArchive::new(reader)?; 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_file_path, question_policy, - logger.clone(), )? { files } else { @@ -64,14 +62,11 @@ 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 - logger.info( - format!( - "Successfully decompressed archive in {} ({} files).", - nice_directory_display(output_dir), - files_unpacked - ), - true, - ); + info_accessible(format!( + "Successfully decompressed archive in {} ({} files).", + nice_directory_display(output_dir), + files_unpacked + )); return Ok(()); } @@ -115,11 +110,10 @@ pub fn decompress_file( } Tar => { 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_file_path, question_policy, - logger.clone(), )? { files } else { @@ -140,11 +134,10 @@ pub fn decompress_file( let zip_archive = zip::ZipArchive::new(io::Cursor::new(vec))?; 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_file_path, question_policy, - logger.clone(), )? { files } else { @@ -162,18 +155,12 @@ pub fn decompress_file( crate::archive::rar::unpack_archive(temp_file.path(), output_dir, quiet, logger_clone) }) } else { - Box::new(|output_dir| { - crate::archive::rar::unpack_archive(input_file_path, output_dir, quiet, logger.clone()) - }) + Box::new(|output_dir| crate::archive::rar::unpack_archive(input_file_path, output_dir, quiet)) }; - if let ControlFlow::Continue(files) = smart_unpack( - unpack_fn, - output_dir, - &output_file_path, - question_policy, - logger.clone(), - )? { + if let ControlFlow::Continue(files) = + smart_unpack(unpack_fn, output_dir, &output_file_path, question_policy)? + { files } else { return Ok(()); @@ -196,13 +183,10 @@ pub fn decompress_file( io::copy(&mut reader, &mut vec)?; if let ControlFlow::Continue(files) = smart_unpack( - |output_dir| { - crate::archive::sevenz::decompress_sevenz(io::Cursor::new(vec), output_dir, quiet, logger.clone()) - }, + |output_dir| crate::archive::sevenz::decompress_sevenz(io::Cursor::new(vec), output_dir, quiet), output_dir, &output_file_path, question_policy, - logger.clone(), )? { files } else { @@ -215,14 +199,11 @@ 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 - logger.info( - format!( - "Successfully decompressed archive in {}.", - nice_directory_display(output_dir) - ), - true, - ); - logger.info(format!("Files unpacked: {}", files_unpacked), true); + info_accessible(format!( + "Successfully decompressed archive in {}.", + nice_directory_display(output_dir) + )); + info_accessible(format!("Files unpacked: {}", files_unpacked)); Ok(()) } @@ -237,19 +218,15 @@ fn smart_unpack( output_dir: &Path, output_file_path: &Path, question_policy: QuestionPolicy, - logger: Logger, ) -> crate::Result> { assert!(output_dir.exists()); let temp_dir = tempfile::tempdir_in(output_dir)?; let temp_dir_path = temp_dir.path(); - logger.info( - format!( - "Created temporary directory {} to hold decompressed elements.", - nice_directory_display(temp_dir_path) - ), - true, - ); + info_accessible(format!( + "Created temporary directory {} to hold decompressed elements.", + nice_directory_display(temp_dir_path) + )); let files = unpack_fn(temp_dir_path)?; @@ -268,14 +245,11 @@ fn smart_unpack( } fs::rename(&file_path, &correct_path)?; - logger.info( - format!( - "Successfully moved {} to {}.", - nice_directory_display(&file_path), - nice_directory_display(&correct_path) - ), - true, - ); + info_accessible(format!( + "Successfully moved {} to {}.", + nice_directory_display(&file_path), + nice_directory_display(&correct_path) + )); } else { // Multiple files in the root directory, so: // Rename the temporary directory to the archive name, which is output_file_path @@ -284,14 +258,11 @@ fn smart_unpack( return Ok(ControlFlow::Break(())); } fs::rename(temp_dir_path, output_file_path)?; - logger.info( - format!( - "Successfully moved {} to {}.", - nice_directory_display(temp_dir_path), - nice_directory_display(output_file_path) - ), - true, - ); + info_accessible(format!( + "Successfully moved {} to {}.", + nice_directory_display(temp_dir_path), + nice_directory_display(output_file_path) + )); } Ok(ControlFlow::Continue(files)) diff --git a/src/commands/list.rs b/src/commands/list.rs index 07b7029..6e46363 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -9,7 +9,7 @@ use crate::{ commands::warn_user_about_loading_zip_in_memory, extension::CompressionFormat::{self, *}, list::{self, FileInArchive, ListOptions}, - utils::{logger::Logger, user_wants_to_continue}, + utils::user_wants_to_continue, QuestionAction, QuestionPolicy, BUFFER_CAPACITY, }; @@ -20,7 +20,6 @@ pub fn list_archive_contents( formats: Vec, list_options: ListOptions, question_policy: QuestionPolicy, - logger: Logger, ) -> crate::Result<()> { let reader = fs::File::open(archive_path)?; diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 2bb6f3f..5a6af18 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -7,7 +7,7 @@ mod list; use std::{ ops::ControlFlow, path::PathBuf, - sync::{mpsc::channel, Arc, Condvar, Mutex}, + sync::{mpsc, Arc, Condvar, Mutex, OnceLock}, }; use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; @@ -22,7 +22,7 @@ use crate::{ list::ListOptions, utils::{ self, - logger::{map_message, Logger, PrintMessage}, + logger::{map_message, setup_channel, PrintMessage}, to_utf, EscapedPathDisplay, FileVisibilityPolicy, }, CliArgs, QuestionPolicy, @@ -57,8 +57,7 @@ pub fn run( question_policy: QuestionPolicy, file_visibility_policy: FileVisibilityPolicy, ) -> crate::Result<()> { - let (log_sender, log_receiver) = channel::(); - let logger = Logger::new(log_sender); + let log_receiver = setup_channel(); let pair = Arc::new((Mutex::new(false), Condvar::new())); let pair2 = Arc::clone(&pair); @@ -119,7 +118,7 @@ pub fn run( let parsed_formats = parse_format(&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( @@ -152,7 +151,6 @@ pub fn run( question_policy, file_visibility_policy, level, - logger.clone(), ); 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 // as screen readers may not read a commands exit code, making it hard to reason // 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 { // If Ok(false) or Err() occurred, delete incomplete file at `output_path` // @@ -198,12 +196,9 @@ pub fn run( } } else { for path in files.iter() { - let (pathbase, mut file_formats) = - extension::separate_known_extensions_from_name(path, logger.clone()); + let (pathbase, mut file_formats) = extension::separate_known_extensions_from_name(path); - if let ControlFlow::Break(_) = - check::check_mime_type(path, &mut file_formats, question_policy, logger.clone())? - { + if let ControlFlow::Break(_) = check::check_mime_type(path, &mut file_formats, question_policy)? { return Ok(()); } @@ -217,7 +212,7 @@ pub fn run( // 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 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 } else { PathBuf::from(".") @@ -236,7 +231,6 @@ pub fn run( output_file_path, question_policy, args.quiet, - logger.clone(), ) })?; } @@ -250,11 +244,9 @@ pub fn run( } } else { 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(_) = - check::check_mime_type(path, &mut file_formats, question_policy, logger.clone())? - { + if let ControlFlow::Break(_) = check::check_mime_type(path, &mut file_formats, question_policy)? { return Ok(()); } @@ -272,7 +264,7 @@ pub fn run( println!(); } 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)?; } } } diff --git a/src/extension.rs b/src/extension.rs index 5ca9cbe..9e532d7 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -169,7 +169,7 @@ pub fn parse_format(fmt: &OsStr) -> crate::Result> { /// Extracts extensions from a path. /// /// Returns both the remaining path and the list of extension objects -pub fn separate_known_extensions_from_name(path: &Path, logger: Logger) -> (&Path, Vec) { +pub fn separate_known_extensions_from_name(path: &Path) -> (&Path, Vec) { let mut extensions = vec![]; 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 -pub fn extensions_from_path(path: &Path, logger: Logger) -> Vec { - let (_, extensions) = separate_known_extensions_from_name(path, logger); +pub fn extensions_from_path(path: &Path) -> Vec { + let (_, extensions) = separate_known_extensions_from_name(path); extensions } @@ -260,7 +260,7 @@ mod tests { let path = Path::new("bolovo.tar.gz"); - let extensions: Vec = extensions_from_path(path, logger); + let extensions: Vec = extensions_from_path(path); let formats: Vec = flatten_compression_formats(&extensions); assert_eq!(formats, vec![Tar, Gzip]); diff --git a/src/utils/fs.rs b/src/utils/fs.rs index b710305..0fe4807 100644 --- a/src/utils/fs.rs +++ b/src/utils/fs.rs @@ -8,7 +8,7 @@ use std::{ 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}; /// 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. -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() { fs::create_dir_all(path)?; // creating a directory is an important change to the file system we // should always inform the user about - logger.info(format!("Directory {} created.", EscapedPathDisplay::new(path)), true); + info_accessible(format!("Directory {} created.", EscapedPathDisplay::new(path))); } Ok(()) } diff --git a/src/utils/logger.rs b/src/utils/logger.rs index bf02c1b..b5714fe 100644 --- a/src/utils/logger.rs +++ b/src/utils/logger.rs @@ -1,10 +1,26 @@ -use std::sync::mpsc::Sender; +use std::sync::{ + mpsc::{channel, Receiver, Sender}, + OnceLock, +}; use super::colors::{ORANGE, RESET, YELLOW}; use crate::accessible::is_running_in_accessible_mode; +static SENDER: OnceLock> = OnceLock::new(); + +pub fn setup_channel() -> Receiver { + let (tx, rx) = channel(); + SENDER.set(tx).expect("`setup_channel` should only be called once"); + rx +} + +#[track_caller] +fn get_sender() -> &'static Sender { + 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. -/// See +/// See #[derive(Debug)] pub struct PrintMessage { contents: String, @@ -37,6 +53,48 @@ pub fn map_message(msg: &PrintMessage) -> Option { } } +/// 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)] pub struct Logger { log_sender: Sender, @@ -47,7 +105,28 @@ impl Logger { 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 .send(PrintMessage { contents, @@ -61,7 +140,8 @@ impl Logger { self.log_sender .send(PrintMessage { contents, - accessible: true, // does not matter + // Warnings are important and unlikely to flood, so they should be displayed + accessible: true, level: MessageLevel::Warning, }) .unwrap();