Improve logging interface

This commit is contained in:
Antonios Barotsis 2024-03-12 03:33:31 +01:00 committed by João Marcos
parent b04122a6de
commit 1e56bb8f1f
14 changed files with 258 additions and 411 deletions

View File

@ -1,23 +1,14 @@
//! Contains RAR-specific building and unpacking functions //! Contains RAR-specific building and unpacking functions
use std::{path::Path, sync::mpsc::Sender}; use std::path::Path;
use unrar::Archive; use unrar::Archive;
use crate::{ use crate::{error::Error, list::FileInArchive, utils::logger::Logger};
error::Error,
list::FileInArchive,
utils::message::{MessageLevel, PrintMessage},
};
/// 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( pub fn unpack_archive(archive_path: &Path, output_folder: &Path, quiet: bool, logger: Logger) -> crate::Result<usize> {
archive_path: &Path,
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 = Archive::new(archive_path).open_for_processing()?; let mut archive = Archive::new(archive_path).open_for_processing()?;
@ -27,13 +18,10 @@ pub fn unpack_archive(
let entry = header.entry(); let entry = header.entry();
archive = if entry.is_file() { archive = if entry.is_file() {
if !quiet { if !quiet {
log_sender logger.info(
.send(PrintMessage { format!("{} extracted. ({})", entry.filename.display(), entry.unpacked_size),
contents: format!("{} extracted. ({})", entry.filename.display(), entry.unpacked_size), false,
accessible: false, );
level: MessageLevel::Info,
})
.unwrap();
} }
unpacked += 1; unpacked += 1;
header.extract_with_base(output_folder)? header.extract_with_base(output_folder)?

View File

@ -4,7 +4,6 @@ use std::{
env, env,
io::{self, Read, Seek, Write}, io::{self, Read, Seek, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::mpsc::Sender,
}; };
use fs_err as fs; use fs_err as fs;
@ -12,11 +11,7 @@ use same_file::Handle;
use crate::{ use crate::{
error::FinalError, error::FinalError,
utils::{ utils::{self, cd_into_same_dir_as, logger::Logger, Bytes, EscapedPathDisplay, FileVisibilityPolicy},
self, cd_into_same_dir_as,
message::{MessageLevel, PrintMessage},
Bytes, EscapedPathDisplay, FileVisibilityPolicy,
},
}; };
pub fn compress_sevenz<W>( pub fn compress_sevenz<W>(
@ -25,7 +20,7 @@ pub fn compress_sevenz<W>(
writer: W, writer: W,
file_visibility_policy: FileVisibilityPolicy, file_visibility_policy: FileVisibilityPolicy,
quiet: bool, quiet: bool,
log_sender: Sender<PrintMessage>, logger: Logger,
) -> crate::Result<W> ) -> crate::Result<W>
where where
W: Write + Seek, W: Write + Seek,
@ -47,16 +42,11 @@ where
// If the output_path is the same as the input file, warn the user and skip the input (in order to avoid compression recursion) // If the output_path is the same as the input file, warn the user and skip the input (in order to avoid compression recursion)
if let Ok(handle) = &output_handle { if let Ok(handle) = &output_handle {
if matches!(Handle::from_path(path), Ok(x) if &x == handle) { if matches!(Handle::from_path(path), Ok(x) if &x == handle) {
log_sender logger.warning(format!(
.send(PrintMessage { "The output file and the input file are the same: `{}`, skipping...",
contents: format!( output_path.display()
"The output file and the input file are the same: `{}`, skipping...", ));
output_path.display()
),
accessible: true,
level: MessageLevel::Warning,
})
.unwrap();
continue; continue;
} }
} }
@ -66,13 +56,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 {
log_sender logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path)), false);
.send(PrintMessage {
contents: format!("Compressing '{}'.", EscapedPathDisplay::new(path)),
accessible: false,
level: MessageLevel::Info,
})
.unwrap();
} }
let metadata = match path.metadata() { let metadata = match path.metadata() {
@ -109,12 +93,7 @@ where
Ok(bytes) Ok(bytes)
} }
pub fn decompress_sevenz<R>( pub fn decompress_sevenz<R>(reader: R, output_path: &Path, quiet: bool, logger: Logger) -> crate::Result<usize>
reader: R,
output_path: &Path,
quiet: bool,
log_sender: Sender<PrintMessage>,
) -> crate::Result<usize>
where where
R: Read + Seek, R: Read + Seek,
{ {
@ -130,26 +109,20 @@ where
if entry.is_directory() { if entry.is_directory() {
if !quiet { if !quiet {
log_sender logger.info(
.send(PrintMessage { format!("File {} extracted to \"{}\"", entry.name(), file_path.display()),
contents: format!("File {} extracted to \"{}\"", entry.name(), file_path.display()), false,
accessible: false, );
level: MessageLevel::Info,
})
.unwrap();
} }
if !path.exists() { if !path.exists() {
fs::create_dir_all(path)?; fs::create_dir_all(path)?;
} }
} else { } else {
if !quiet { if !quiet {
log_sender logger.info(
.send(PrintMessage { format!("{:?} extracted. ({})", file_path.display(), Bytes::new(entry.size())),
contents: format!("{:?} extracted. ({})", file_path.display(), Bytes::new(entry.size()),), false,
accessible: false, );
level: MessageLevel::Info,
})
.unwrap();
} }
if let Some(parent) = path.parent() { if let Some(parent) = path.parent() {

View File

@ -4,7 +4,7 @@ use std::{
env, env,
io::prelude::*, io::prelude::*,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::mpsc::{self, Receiver, Sender}, sync::mpsc::{self, Receiver},
thread, thread,
}; };
@ -14,11 +14,7 @@ use same_file::Handle;
use crate::{ use crate::{
error::FinalError, error::FinalError,
list::FileInArchive, list::FileInArchive,
utils::{ utils::{self, logger::Logger, Bytes, EscapedPathDisplay, FileVisibilityPolicy},
self,
message::{MessageLevel, PrintMessage},
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`.
@ -27,7 +23,7 @@ pub fn unpack_archive(
reader: Box<dyn Read>, reader: Box<dyn Read>,
output_folder: &Path, output_folder: &Path,
quiet: bool, quiet: bool,
log_sender: Sender<PrintMessage>, logger: Logger,
) -> crate::Result<usize> { ) -> 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);
@ -43,17 +39,14 @@ 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 {
log_sender logger.info(
.send(PrintMessage { format!(
contents: 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,
accessible: false, );
level: MessageLevel::Info,
})
.unwrap();
files_unpacked += 1; files_unpacked += 1;
} }
@ -98,7 +91,7 @@ pub fn build_archive_from_paths<W>(
writer: W, writer: W,
file_visibility_policy: FileVisibilityPolicy, file_visibility_policy: FileVisibilityPolicy,
quiet: bool, quiet: bool,
log_sender: Sender<PrintMessage>, logger: Logger,
) -> crate::Result<W> ) -> crate::Result<W>
where where
W: Write, W: Write,
@ -120,16 +113,10 @@ where
// If the output_path is the same as the input file, warn the user and skip the input (in order to avoid compression recursion) // If the output_path is the same as the input file, warn the user and skip the input (in order to avoid compression recursion)
if let Ok(handle) = &output_handle { if let Ok(handle) = &output_handle {
if matches!(Handle::from_path(path), Ok(x) if &x == handle) { if matches!(Handle::from_path(path), Ok(x) if &x == handle) {
log_sender logger.warning(format!(
.send(PrintMessage { "The output file and the input file are the same: `{}`, skipping...",
contents: format!( output_path.display()
"The output file and the input file are the same: `{}`, skipping...", ));
output_path.display()
),
accessible: true,
level: MessageLevel::Warning,
})
.unwrap();
continue; continue;
} }
@ -140,13 +127,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 {
log_sender logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path)), false);
.send(PrintMessage {
contents: format!("Compressing '{}'.", EscapedPathDisplay::new(path)),
accessible: false,
level: MessageLevel::Info,
})
.unwrap();
} }
if path.is_dir() { if path.is_dir() {

View File

@ -6,7 +6,7 @@ use std::{
env, env,
io::{self, prelude::*}, io::{self, prelude::*},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::mpsc::{self, Sender}, sync::mpsc,
thread, thread,
}; };
@ -20,9 +20,8 @@ use crate::{
error::FinalError, error::FinalError,
list::FileInArchive, list::FileInArchive,
utils::{ utils::{
self, cd_into_same_dir_as, get_invalid_utf8_paths, self, cd_into_same_dir_as, get_invalid_utf8_paths, logger::Logger, pretty_format_list_of_paths, strip_cur_dir,
message::{MessageLevel, PrintMessage}, Bytes, EscapedPathDisplay, FileVisibilityPolicy,
pretty_format_list_of_paths, strip_cur_dir, Bytes, EscapedPathDisplay, FileVisibilityPolicy,
}, },
}; };
@ -32,7 +31,7 @@ pub fn unpack_archive<R>(
mut archive: ZipArchive<R>, mut archive: ZipArchive<R>,
output_folder: &Path, output_folder: &Path,
quiet: bool, quiet: bool,
log_sender: Sender<PrintMessage>, logger: Logger,
) -> crate::Result<usize> ) -> crate::Result<usize>
where where
R: Read + Seek, R: Read + Seek,
@ -50,7 +49,7 @@ where
let file_path = output_folder.join(file_path); let file_path = output_folder.join(file_path);
display_zip_comment_if_exists(&file, log_sender.clone()); display_zip_comment_if_exists(&file, logger.clone());
match file.name().ends_with('/') { match file.name().ends_with('/') {
_is_dir @ true => { _is_dir @ true => {
@ -59,13 +58,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 {
log_sender logger.info(format!("File {} extracted to \"{}\"", idx, file_path.display()), false);
.send(PrintMessage {
contents: format!("File {} extracted to \"{}\"", idx, file_path.display()),
accessible: false,
level: MessageLevel::Info,
})
.unwrap();
} }
fs::create_dir_all(&file_path)?; fs::create_dir_all(&file_path)?;
} }
@ -79,13 +72,10 @@ 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 {
log_sender logger.info(
.send(PrintMessage { format!("{:?} extracted. ({})", file_path.display(), Bytes::new(file.size())),
contents: format!("{:?} extracted. ({})", file_path.display(), Bytes::new(file.size()),), false,
accessible: false, );
level: MessageLevel::Info,
})
.unwrap();
} }
let mut output_file = fs::File::create(file_path)?; let mut output_file = fs::File::create(file_path)?;
@ -148,7 +138,7 @@ pub fn build_archive_from_paths<W>(
writer: W, writer: W,
file_visibility_policy: FileVisibilityPolicy, file_visibility_policy: FileVisibilityPolicy,
quiet: bool, quiet: bool,
log_sender: Sender<PrintMessage>, logger: Logger,
) -> crate::Result<W> ) -> crate::Result<W>
where where
W: Write + Seek, W: Write + Seek,
@ -190,18 +180,10 @@ where
// If the output_path is the same as the input file, warn the user and skip the input (in order to avoid compression recursion) // If the output_path is the same as the input file, warn the user and skip the input (in order to avoid compression recursion)
if let Ok(handle) = &output_handle { if let Ok(handle) = &output_handle {
if matches!(Handle::from_path(path), Ok(x) if &x == handle) { if matches!(Handle::from_path(path), Ok(x) if &x == handle) {
log_sender logger.warning(format!(
.send(PrintMessage { "The output file and the input file are the same: `{}`, skipping...",
contents: format!( output_path.display()
"The output file and the input file are the same: `{}`, skipping...", ));
output_path.display()
),
accessible: true,
level: MessageLevel::Warning,
})
.unwrap();
continue;
} }
} }
@ -210,13 +192,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 {
log_sender logger.info(format!("Compressing '{}'.", EscapedPathDisplay::new(path)), false);
.send(PrintMessage {
contents: format!("Compressing '{}'.", EscapedPathDisplay::new(path)),
accessible: false,
level: MessageLevel::Info,
})
.unwrap();
} }
let metadata = match path.metadata() { let metadata = match path.metadata() {
@ -266,7 +242,7 @@ where
Ok(bytes) Ok(bytes)
} }
fn display_zip_comment_if_exists(file: &ZipFile, log_sender: Sender<PrintMessage>) { fn display_zip_comment_if_exists(file: &ZipFile, logger: Logger) {
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,
@ -279,13 +255,7 @@ fn display_zip_comment_if_exists(file: &ZipFile, log_sender: Sender<PrintMessage
// 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..
log_sender logger.info(format!("Found comment in {}: {}", file.name(), comment), true);
.send(PrintMessage {
contents: format!("Found comment in {}: {}", file.name(), comment),
accessible: true,
level: MessageLevel::Info,
})
.unwrap();
} }
} }

View File

@ -6,15 +6,13 @@ use std::{
ffi::OsString, ffi::OsString,
ops::ControlFlow, ops::ControlFlow,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::mpsc::Sender,
}; };
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::{
message::{MessageLevel, PrintMessage}, logger::Logger, pretty_format_list_of_paths, try_infer_extension, user_wants_to_continue, EscapedPathDisplay,
pretty_format_list_of_paths, try_infer_extension, user_wants_to_continue, EscapedPathDisplay,
}, },
QuestionAction, QuestionPolicy, Result, QuestionAction, QuestionPolicy, Result,
}; };
@ -29,7 +27,7 @@ pub fn check_mime_type(
path: &Path, path: &Path,
formats: &mut Vec<Extension>, formats: &mut Vec<Extension>,
question_policy: QuestionPolicy, question_policy: QuestionPolicy,
log_sender: Sender<PrintMessage>, logger: Logger,
) -> Result<ControlFlow<()>> { ) -> Result<ControlFlow<()>> {
if formats.is_empty() { if formats.is_empty() {
// File with no extension // File with no extension
@ -37,13 +35,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.
log_sender logger.info(
.send(PrintMessage { format!("Detected file: `{}` extension as `{}`", path.display(), detected_format),
contents: format!("Detected file: `{}` extension as `{}`", path.display(), detected_format), true,
accessible: true, );
level: MessageLevel::Info,
})
.unwrap();
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);
} else { } else {
@ -59,16 +55,11 @@ pub fn check_mime_type(
.compression_formats .compression_formats
.ends_with(detected_format.compression_formats) .ends_with(detected_format.compression_formats)
{ {
log_sender logger.warning(format!(
.send(PrintMessage { "The file extension: `{}` differ from the detected extension: `{}`",
contents: format!( outer_ext, detected_format
"The file extension: `{}` differ from the detected extension: `{}`", ));
outer_ext, detected_format
),
accessible: true,
level: MessageLevel::Warning,
})
.unwrap();
if !user_wants_to_continue(path, question_policy, QuestionAction::Decompression)? { if !user_wants_to_continue(path, question_policy, QuestionAction::Decompression)? {
return Ok(ControlFlow::Break(())); return Ok(ControlFlow::Break(()));
} }
@ -76,16 +67,13 @@ 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.
log_sender logger.info(
.send(PrintMessage { format!(
contents: 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,
accessible: true, );
level: MessageLevel::Info,
})
.unwrap();
} }
Ok(ControlFlow::Continue(())) Ok(ControlFlow::Continue(()))
} }

View File

@ -1,7 +1,6 @@
use std::{ use std::{
io::{self, BufWriter, Cursor, Seek, Write}, io::{self, BufWriter, Cursor, Seek, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::mpsc::Sender,
}; };
use fs_err as fs; use fs_err as fs;
@ -11,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::{message::PrintMessage, user_wants_to_continue, FileVisibilityPolicy}, utils::{logger::Logger, user_wants_to_continue, FileVisibilityPolicy},
QuestionAction, QuestionPolicy, BUFFER_CAPACITY, QuestionAction, QuestionPolicy, BUFFER_CAPACITY,
}; };
@ -35,7 +34,7 @@ pub fn compress_files(
question_policy: QuestionPolicy, question_policy: QuestionPolicy,
file_visibility_policy: FileVisibilityPolicy, file_visibility_policy: FileVisibilityPolicy,
level: Option<i16>, level: Option<i16>,
log_sender: Sender<PrintMessage>, 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);
@ -107,13 +106,13 @@ pub fn compress_files(
&mut writer, &mut writer,
file_visibility_policy, file_visibility_policy,
quiet, quiet,
log_sender.clone(), logger.clone(),
)?; )?;
writer.flush()?; writer.flush()?;
} }
Zip => { Zip => {
if !formats.is_empty() { if !formats.is_empty() {
warn_user_about_loading_zip_in_memory(log_sender.clone()); warn_user_about_loading_zip_in_memory(logger.clone());
if !user_wants_to_continue(output_path, question_policy, QuestionAction::Compression)? { if !user_wants_to_continue(output_path, question_policy, QuestionAction::Compression)? {
return Ok(false); return Ok(false);
@ -128,7 +127,7 @@ pub fn compress_files(
&mut vec_buffer, &mut vec_buffer,
file_visibility_policy, file_visibility_policy,
quiet, quiet,
log_sender.clone(), logger.clone(),
)?; )?;
vec_buffer.rewind()?; vec_buffer.rewind()?;
io::copy(&mut vec_buffer, &mut writer)?; io::copy(&mut vec_buffer, &mut writer)?;
@ -142,7 +141,7 @@ pub fn compress_files(
} }
SevenZip => { SevenZip => {
if !formats.is_empty() { if !formats.is_empty() {
warn_user_about_loading_sevenz_in_memory(log_sender.clone()); warn_user_about_loading_sevenz_in_memory(logger.clone());
if !user_wants_to_continue(output_path, question_policy, QuestionAction::Compression)? { if !user_wants_to_continue(output_path, question_policy, QuestionAction::Compression)? {
return Ok(false); return Ok(false);
@ -156,7 +155,7 @@ pub fn compress_files(
&mut vec_buffer, &mut vec_buffer,
file_visibility_policy, file_visibility_policy,
quiet, quiet,
log_sender.clone(), logger.clone(),
)?; )?;
vec_buffer.rewind()?; vec_buffer.rewind()?;
io::copy(&mut vec_buffer, &mut writer)?; io::copy(&mut vec_buffer, &mut writer)?;

View File

@ -2,7 +2,6 @@ use std::{
io::{self, BufReader, Read}, io::{self, BufReader, Read},
ops::ControlFlow, ops::ControlFlow,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::mpsc::Sender,
}; };
use fs_err as fs; use fs_err as fs;
@ -14,11 +13,7 @@ use crate::{
CompressionFormat::{self, *}, CompressionFormat::{self, *},
Extension, Extension,
}, },
utils::{ utils::{self, logger::Logger, nice_directory_display, user_wants_to_continue},
self,
message::{MessageLevel, PrintMessage},
nice_directory_display, user_wants_to_continue,
},
QuestionAction, QuestionPolicy, BUFFER_CAPACITY, QuestionAction, QuestionPolicy, BUFFER_CAPACITY,
}; };
@ -35,7 +30,7 @@ pub fn decompress_file(
output_file_path: PathBuf, output_file_path: PathBuf,
question_policy: QuestionPolicy, question_policy: QuestionPolicy,
quiet: bool, quiet: bool,
log_sender: Sender<PrintMessage>, 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)?;
@ -54,11 +49,11 @@ 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, log_sender.clone()), |output_dir| crate::archive::zip::unpack_archive(zip_archive, output_dir, quiet, logger.clone()),
output_dir, output_dir,
&output_file_path, &output_file_path,
question_policy, question_policy,
log_sender.clone(), logger.clone(),
)? { )? {
files files
} else { } else {
@ -69,17 +64,14 @@ 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 logger.info(
.send(PrintMessage { format!(
contents: 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,
accessible: true, );
level: MessageLevel::Info,
})
.unwrap();
return Ok(()); return Ok(());
} }
@ -123,11 +115,11 @@ 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, log_sender.clone()), |output_dir| crate::archive::tar::unpack_archive(reader, output_dir, quiet, logger.clone()),
output_dir, output_dir,
&output_file_path, &output_file_path,
question_policy, question_policy,
log_sender.clone(), logger.clone(),
)? { )? {
files files
} else { } else {
@ -136,7 +128,7 @@ pub fn decompress_file(
} }
Zip => { Zip => {
if formats.len() > 1 { if formats.len() > 1 {
warn_user_about_loading_zip_in_memory(log_sender.clone()); warn_user_about_loading_zip_in_memory(logger.clone());
if !user_wants_to_continue(input_file_path, question_policy, QuestionAction::Decompression)? { if !user_wants_to_continue(input_file_path, question_policy, QuestionAction::Decompression)? {
return Ok(()); return Ok(());
@ -148,11 +140,11 @@ 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, log_sender.clone()), |output_dir| crate::archive::zip::unpack_archive(zip_archive, output_dir, quiet, logger.clone()),
output_dir, output_dir,
&output_file_path, &output_file_path,
question_policy, question_policy,
log_sender.clone(), logger.clone(),
)? { )? {
files files
} else { } else {
@ -165,13 +157,13 @@ pub fn decompress_file(
let unpack_fn: Box<dyn FnOnce(&Path) -> UnpackResult> = if formats.len() > 1 { let unpack_fn: Box<dyn FnOnce(&Path) -> UnpackResult> = if formats.len() > 1 {
let mut temp_file = tempfile::NamedTempFile::new()?; let mut temp_file = tempfile::NamedTempFile::new()?;
io::copy(&mut reader, &mut temp_file)?; io::copy(&mut reader, &mut temp_file)?;
let log_sender_clone = log_sender.clone(); let logger_clone = logger.clone();
Box::new(move |output_dir| { Box::new(move |output_dir| {
crate::archive::rar::unpack_archive(temp_file.path(), output_dir, quiet, log_sender_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, log_sender.clone()) crate::archive::rar::unpack_archive(input_file_path, output_dir, quiet, logger.clone())
}) })
}; };
@ -180,7 +172,7 @@ pub fn decompress_file(
output_dir, output_dir,
&output_file_path, &output_file_path,
question_policy, question_policy,
log_sender.clone(), logger.clone(),
)? { )? {
files files
} else { } else {
@ -193,7 +185,7 @@ pub fn decompress_file(
} }
SevenZip => { SevenZip => {
if formats.len() > 1 { if formats.len() > 1 {
warn_user_about_loading_sevenz_in_memory(log_sender.clone()); warn_user_about_loading_sevenz_in_memory(logger.clone());
if !user_wants_to_continue(input_file_path, question_policy, QuestionAction::Decompression)? { if !user_wants_to_continue(input_file_path, question_policy, QuestionAction::Decompression)? {
return Ok(()); return Ok(());
@ -205,17 +197,12 @@ pub fn decompress_file(
if let ControlFlow::Continue(files) = smart_unpack( if let ControlFlow::Continue(files) = smart_unpack(
|output_dir| { |output_dir| {
crate::archive::sevenz::decompress_sevenz( crate::archive::sevenz::decompress_sevenz(io::Cursor::new(vec), output_dir, quiet, logger.clone())
io::Cursor::new(vec),
output_dir,
quiet,
log_sender.clone(),
)
}, },
output_dir, output_dir,
&output_file_path, &output_file_path,
question_policy, question_policy,
log_sender.clone(), logger.clone(),
)? { )? {
files files
} else { } else {
@ -228,23 +215,14 @@ 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 logger.info(
.send(PrintMessage { format!(
contents: format!( "Successfully decompressed archive in {}.",
"Successfully decompressed archive in {}.", nice_directory_display(output_dir)
nice_directory_display(output_dir) ),
), true,
accessible: true, );
level: MessageLevel::Info, logger.info(format!("Files unpacked: {}", files_unpacked), true);
})
.unwrap();
log_sender
.send(PrintMessage {
contents: format!("Files unpacked: {}", files_unpacked),
accessible: true,
level: MessageLevel::Info,
})
.unwrap();
Ok(()) Ok(())
} }
@ -259,22 +237,19 @@ fn smart_unpack(
output_dir: &Path, output_dir: &Path,
output_file_path: &Path, output_file_path: &Path,
question_policy: QuestionPolicy, question_policy: QuestionPolicy,
log_sender: Sender<PrintMessage>, 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();
log_sender logger.info(
.send(PrintMessage { format!(
contents: 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,
accessible: true, );
level: MessageLevel::Info,
})
.unwrap();
let files = unpack_fn(temp_dir_path)?; let files = unpack_fn(temp_dir_path)?;
@ -293,17 +268,14 @@ fn smart_unpack(
} }
fs::rename(&file_path, &correct_path)?; fs::rename(&file_path, &correct_path)?;
log_sender logger.info(
.send(PrintMessage { format!(
contents: 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,
accessible: true, );
level: MessageLevel::Info,
})
.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
@ -312,17 +284,14 @@ 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 logger.info(
.send(PrintMessage { format!(
contents: 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,
accessible: true, );
level: MessageLevel::Info,
})
.unwrap();
} }
Ok(ControlFlow::Continue(files)) Ok(ControlFlow::Continue(files))

View File

@ -1,7 +1,6 @@
use std::{ use std::{
io::{self, BufReader, Read}, io::{self, BufReader, Read},
path::Path, path::Path,
sync::mpsc::Sender,
}; };
use fs_err as fs; use fs_err as fs;
@ -10,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::{message::PrintMessage, user_wants_to_continue}, utils::{logger::Logger, user_wants_to_continue},
QuestionAction, QuestionPolicy, BUFFER_CAPACITY, QuestionAction, QuestionPolicy, BUFFER_CAPACITY,
}; };
@ -21,7 +20,7 @@ pub fn list_archive_contents(
formats: Vec<CompressionFormat>, formats: Vec<CompressionFormat>,
list_options: ListOptions, list_options: ListOptions,
question_policy: QuestionPolicy, question_policy: QuestionPolicy,
log_sender: Sender<PrintMessage>, logger: Logger,
) -> crate::Result<()> { ) -> crate::Result<()> {
let reader = fs::File::open(archive_path)?; let reader = fs::File::open(archive_path)?;
@ -67,7 +66,7 @@ pub fn list_archive_contents(
Tar => Box::new(crate::archive::tar::list_archive(tar::Archive::new(reader))), Tar => Box::new(crate::archive::tar::list_archive(tar::Archive::new(reader))),
Zip => { Zip => {
if formats.len() > 1 { if formats.len() > 1 {
warn_user_about_loading_zip_in_memory(log_sender.clone()); warn_user_about_loading_zip_in_memory(logger.clone());
if !user_wants_to_continue(archive_path, question_policy, QuestionAction::Decompression)? { if !user_wants_to_continue(archive_path, question_policy, QuestionAction::Decompression)? {
return Ok(()); return Ok(());
@ -96,7 +95,7 @@ pub fn list_archive_contents(
} }
SevenZip => { SevenZip => {
if formats.len() > 1 { if formats.len() > 1 {
warn_user_about_loading_zip_in_memory(log_sender.clone()); warn_user_about_loading_zip_in_memory(logger.clone());
if !user_wants_to_continue(archive_path, question_policy, QuestionAction::Decompression)? { if !user_wants_to_continue(archive_path, question_policy, QuestionAction::Decompression)? {
return Ok(()); return Ok(());
} }

View File

@ -7,17 +7,13 @@ mod list;
use std::{ use std::{
ops::ControlFlow, ops::ControlFlow,
path::PathBuf, path::PathBuf,
sync::{ sync::{mpsc::channel, Arc, Condvar, Mutex},
mpsc::{channel, Sender},
Arc, Condvar, Mutex,
},
}; };
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use utils::colors; use utils::colors;
use crate::{ use crate::{
accessible::is_running_in_accessible_mode,
check, check,
cli::Subcommand, cli::Subcommand,
commands::{compress::compress_files, decompress::decompress_file, list::list_archive_contents}, commands::{compress::compress_files, decompress::decompress_file, list::list_archive_contents},
@ -26,42 +22,30 @@ use crate::{
list::ListOptions, list::ListOptions,
utils::{ utils::{
self, self,
message::{MessageLevel, PrintMessage}, logger::{map_message, Logger, PrintMessage},
to_utf, EscapedPathDisplay, FileVisibilityPolicy, to_utf, EscapedPathDisplay, FileVisibilityPolicy,
}, },
CliArgs, QuestionPolicy, CliArgs, QuestionPolicy,
}; };
/// Warn the user that (de)compressing this .zip archive might freeze their system. /// Warn the user that (de)compressing this .zip archive might freeze their system.
fn warn_user_about_loading_zip_in_memory(log_sender: Sender<PrintMessage>) { fn warn_user_about_loading_zip_in_memory(logger: Logger) {
const ZIP_IN_MEMORY_LIMITATION_WARNING: &str = "\n\ const ZIP_IN_MEMORY_LIMITATION_WARNING: &str = "\n\
\tThe format '.zip' is limited and cannot be (de)compressed using encoding streams.\n\ \tThe format '.zip' is limited and cannot be (de)compressed using encoding streams.\n\
\tWhen using '.zip' with other formats, (de)compression must be done in-memory\n\ \tWhen using '.zip' with other formats, (de)compression must be done in-memory\n\
\tCareful, you might run out of RAM if the archive is too large!"; \tCareful, you might run out of RAM if the archive is too large!";
log_sender logger.warning(ZIP_IN_MEMORY_LIMITATION_WARNING.to_string());
.send(PrintMessage {
contents: ZIP_IN_MEMORY_LIMITATION_WARNING.to_string(),
accessible: true,
level: MessageLevel::Warning,
})
.unwrap();
} }
/// Warn the user that (de)compressing this .7z archive might freeze their system. /// Warn the user that (de)compressing this .7z archive might freeze their system.
fn warn_user_about_loading_sevenz_in_memory(log_sender: Sender<PrintMessage>) { fn warn_user_about_loading_sevenz_in_memory(logger: Logger) {
const SEVENZ_IN_MEMORY_LIMITATION_WARNING: &str = "\n\ const SEVENZ_IN_MEMORY_LIMITATION_WARNING: &str = "\n\
\tThe format '.7z' is limited and cannot be (de)compressed using encoding streams.\n\ \tThe format '.7z' is limited and cannot be (de)compressed using encoding streams.\n\
\tWhen using '.7z' with other formats, (de)compression must be done in-memory\n\ \tWhen using '.7z' with other formats, (de)compression must be done in-memory\n\
\tCareful, you might run out of RAM if the archive is too large!"; \tCareful, you might run out of RAM if the archive is too large!";
log_sender logger.warning(SEVENZ_IN_MEMORY_LIMITATION_WARNING.to_string());
.send(PrintMessage {
contents: SEVENZ_IN_MEMORY_LIMITATION_WARNING.to_string(),
accessible: true,
level: MessageLevel::Warning,
})
.unwrap();
} }
/// This function checks what command needs to be run and performs A LOT of ahead-of-time checks /// This function checks what command needs to be run and performs A LOT of ahead-of-time checks
@ -74,43 +58,16 @@ pub fn run(
file_visibility_policy: FileVisibilityPolicy, file_visibility_policy: FileVisibilityPolicy,
) -> crate::Result<()> { ) -> crate::Result<()> {
let (log_sender, log_receiver) = channel::<PrintMessage>(); let (log_sender, log_receiver) = channel::<PrintMessage>();
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);
// Log received messages until all senders are dropped // Log received messages until all senders are dropped
rayon::spawn(move || { rayon::spawn(move || {
use utils::colors::{ORANGE, RESET, YELLOW};
const BUFFER_SIZE: usize = 10; const BUFFER_SIZE: usize = 10;
let mut buffer = Vec::<String>::with_capacity(BUFFER_SIZE); let mut buffer = Vec::<String>::with_capacity(BUFFER_SIZE);
// TODO: Move this out to utils
fn map_message(msg: &PrintMessage) -> Option<String> {
match msg.level {
MessageLevel::Info => {
if msg.accessible {
if is_running_in_accessible_mode() {
Some(format!("{}Info:{} {}", *YELLOW, *RESET, msg.contents))
} else {
Some(format!("{}[INFO]{} {}", *YELLOW, *RESET, msg.contents))
}
} else if !is_running_in_accessible_mode() {
Some(format!("{}[INFO]{} {}", *YELLOW, *RESET, msg.contents))
} else {
None
}
}
MessageLevel::Warning => {
if is_running_in_accessible_mode() {
Some(format!("{}Warning:{} ", *ORANGE, *RESET))
} else {
Some(format!("{}[WARNING]{} ", *ORANGE, *RESET))
}
}
}
}
loop { loop {
let msg = log_receiver.recv(); let msg = log_receiver.recv();
@ -124,16 +81,14 @@ pub fn run(
tmp.push_str(&msg); tmp.push_str(&msg);
} }
// TODO: Send this to stderr eprintln!("{}", tmp);
println!("{}", tmp);
buffer.clear(); buffer.clear();
} else if let Some(msg) = map_message(&msg) { } else if let Some(msg) = map_message(&msg) {
buffer.push(msg); buffer.push(msg);
} }
} else { } else {
// All senders have been dropped // All senders have been dropped
// TODO: Send this to stderr eprintln!("{}", buffer.join("\n"));
println!("{}", buffer.join("\n"));
// Wake up the main thread // Wake up the main thread
let (lock, cvar) = &*pair2; let (lock, cvar) = &*pair2;
@ -164,7 +119,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, log_sender.clone())), None => (None, extension::extensions_from_path(&output_path, logger.clone())),
}; };
check::check_invalid_compression_with_non_archive_format( check::check_invalid_compression_with_non_archive_format(
@ -197,7 +152,7 @@ pub fn run(
question_policy, question_policy,
file_visibility_policy, file_visibility_policy,
level, level,
log_sender.clone(), logger.clone(),
); );
if let Ok(true) = compress_result { if let Ok(true) = compress_result {
@ -205,13 +160,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
log_sender logger.info(format!("Successfully compressed '{}'.", to_utf(&output_path)), true);
.send(PrintMessage {
contents: format!("Successfully compressed '{}'.", to_utf(&output_path)),
accessible: true,
level: MessageLevel::Info,
})
.unwrap();
} 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`
// //
@ -250,10 +199,10 @@ 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, log_sender.clone()); 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, log_sender.clone())? check::check_mime_type(path, &mut file_formats, question_policy, logger.clone())?
{ {
return Ok(()); return Ok(());
} }
@ -268,7 +217,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, log_sender.clone())?; utils::create_dir_if_non_existent(&dir, logger.clone())?;
dir dir
} else { } else {
PathBuf::from(".") PathBuf::from(".")
@ -287,7 +236,7 @@ pub fn run(
output_file_path, output_file_path,
question_policy, question_policy,
args.quiet, args.quiet,
log_sender.clone(), logger.clone(),
) )
})?; })?;
} }
@ -301,10 +250,10 @@ pub fn run(
} }
} else { } else {
for path in files.iter() { for path in files.iter() {
let mut file_formats = extension::extensions_from_path(path, log_sender.clone()); let mut file_formats = extension::extensions_from_path(path, logger.clone());
if let ControlFlow::Break(_) = if let ControlFlow::Break(_) =
check::check_mime_type(path, &mut file_formats, question_policy, log_sender.clone())? check::check_mime_type(path, &mut file_formats, question_policy, logger.clone())?
{ {
return Ok(()); return Ok(());
} }
@ -323,13 +272,15 @@ 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, log_sender.clone())?; list_archive_contents(archive_path, formats, list_options, question_policy, logger.clone())?;
} }
} }
} }
// Drop our sender so when all threads are done, no clones are left // Drop our sender so when all threads are done, no clones are left.
drop(log_sender); // This is needed, otherwise the logging thread will never exit since we would be keeping a
// sender alive here.
drop(logger);
// 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.

View File

@ -1,14 +1,11 @@
//! Our representation of all the supported compression formats. //! Our representation of all the supported compression formats.
use std::{ffi::OsStr, fmt, path::Path, sync::mpsc::Sender}; use std::{ffi::OsStr, fmt, path::Path};
use bstr::ByteSlice; use bstr::ByteSlice;
use self::CompressionFormat::*; use self::CompressionFormat::*;
use crate::{ use crate::{error::Error, utils::logger::Logger};
error::Error,
utils::message::{MessageLevel, PrintMessage},
};
pub const SUPPORTED_EXTENSIONS: &[&str] = &[ pub const SUPPORTED_EXTENSIONS: &[&str] = &[
"tar", "tar",
@ -172,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, log_sender: Sender<PrintMessage>) -> (&Path, Vec<Extension>) { pub fn separate_known_extensions_from_name(path: &Path, logger: Logger) -> (&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 {
@ -187,15 +184,9 @@ pub fn separate_known_extensions_from_name(path: &Path, log_sender: Sender<Print
if let Ok(name) = name.to_str() { if let Ok(name) = name.to_str() {
let file_stem = name.trim_matches('.'); let file_stem = name.trim_matches('.');
if SUPPORTED_EXTENSIONS.contains(&file_stem) || SUPPORTED_ALIASES.contains(&file_stem) { if SUPPORTED_EXTENSIONS.contains(&file_stem) || SUPPORTED_ALIASES.contains(&file_stem) {
log_sender logger.warning(format!(
.send(PrintMessage { "Received a file with name '{file_stem}', but {file_stem} was expected as the extension."
contents: format!( ));
"Received a file with name '{file_stem}', but {file_stem} was expected as the extension."
),
accessible: true,
level: MessageLevel::Warning,
})
.unwrap();
} }
} }
@ -203,8 +194,8 @@ pub fn separate_known_extensions_from_name(path: &Path, log_sender: Sender<Print
} }
/// 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, log_sender: Sender<PrintMessage>) -> Vec<Extension> { pub fn extensions_from_path(path: &Path, logger: Logger) -> Vec<Extension> {
let (_, extensions) = separate_known_extensions_from_name(path, log_sender); let (_, extensions) = separate_known_extensions_from_name(path, logger);
extensions extensions
} }
@ -260,14 +251,16 @@ pub fn build_archive_file_suggestion(path: &Path, suggested_extension: &str) ->
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::utils::logger::PrintMessage;
#[test] #[test]
fn test_extensions_from_path() { fn test_extensions_from_path() {
let (log_sender, _log_receiver) = std::sync::mpsc::channel::<PrintMessage>(); let (log_sender, _log_receiver) = std::sync::mpsc::channel::<PrintMessage>();
let logger = Logger::new(log_sender);
let path = Path::new("bolovo.tar.gz"); let path = Path::new("bolovo.tar.gz");
let extensions: Vec<Extension> = extensions_from_path(path, log_sender); let extensions: Vec<Extension> = extensions_from_path(path, logger);
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

@ -4,15 +4,11 @@ use std::{
env, env,
io::Read, io::Read,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::mpsc::Sender,
}; };
use fs_err as fs; use fs_err as fs;
use super::{ use super::{logger::Logger, user_wants_to_overwrite};
message::{MessageLevel, PrintMessage},
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.
@ -40,18 +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, log_sender: Sender<PrintMessage>) -> crate::Result<()> { pub fn create_dir_if_non_existent(path: &Path, logger: Logger) -> 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
log_sender logger.info(format!("Directory {} created.", EscapedPathDisplay::new(path)), true);
.send(PrintMessage {
contents: format!("Directory {} created.", EscapedPathDisplay::new(path)),
accessible: true,
level: MessageLevel::Info,
})
.unwrap();
} }
Ok(()) Ok(())
} }

75
src/utils/logger.rs Normal file
View File

@ -0,0 +1,75 @@
use std::sync::mpsc::Sender;
use super::colors::{ORANGE, RESET, YELLOW};
use crate::accessible::is_running_in_accessible_mode;
/// Message object used for sending logs from worker threads to a logging thread via channels.
/// See <https://github.com/ouch-org/ouch/issues/632>
#[derive(Debug)]
pub struct PrintMessage {
contents: String,
accessible: bool,
level: MessageLevel,
}
pub fn map_message(msg: &PrintMessage) -> Option<String> {
match msg.level {
MessageLevel::Info => {
if msg.accessible {
if is_running_in_accessible_mode() {
Some(format!("{}Info:{} {}", *YELLOW, *RESET, msg.contents))
} else {
Some(format!("{}[INFO]{} {}", *YELLOW, *RESET, msg.contents))
}
} else if !is_running_in_accessible_mode() {
Some(format!("{}[INFO]{} {}", *YELLOW, *RESET, msg.contents))
} else {
None
}
}
MessageLevel::Warning => {
if is_running_in_accessible_mode() {
Some(format!("{}Warning:{} ", *ORANGE, *RESET))
} else {
Some(format!("{}[WARNING]{} ", *ORANGE, *RESET))
}
}
}
}
#[derive(Clone)]
pub struct Logger {
log_sender: Sender<PrintMessage>,
}
impl Logger {
pub fn new(log_sender: Sender<PrintMessage>) -> Self {
Self { log_sender }
}
pub fn info(&self, contents: String, accessible: bool) {
self.log_sender
.send(PrintMessage {
contents,
accessible,
level: MessageLevel::Info,
})
.unwrap();
}
pub fn warning(&self, contents: String) {
self.log_sender
.send(PrintMessage {
contents,
accessible: true, // does not matter
level: MessageLevel::Warning,
})
.unwrap();
}
}
#[derive(Debug, PartialEq)]
pub enum MessageLevel {
Info,
Warning,
}

View File

@ -1,29 +0,0 @@
/// Message object used for sending logs from worker threads to a logging thread via channels.
/// See <https://github.com/ouch-org/ouch/issues/632>
///
/// ## Example
///
/// ```rs
/// // This is already done in the main thread in src/commands/mod.rs
/// // Functions that want to log anything just need to have
/// // `log_sender: Sender<PrintMessage>` as an argument.
/// let (log_sender, log_receiver) = channel::<PrintMessage>();
///
/// log_sender
/// .send(PrintMessage {
/// contents: "Hello, world!".to_string(),
/// accessible: true,
/// }).unwrap();
/// ```
#[derive(Debug)]
pub struct PrintMessage {
pub contents: String,
pub accessible: bool,
pub level: MessageLevel,
}
#[derive(Debug, PartialEq)]
pub enum MessageLevel {
Info,
Warning,
}

View File

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