diff --git a/src/archive/tar.rs b/src/archive/tar.rs index f183698..9d900f0 100644 --- a/src/archive/tar.rs +++ b/src/archive/tar.rs @@ -14,6 +14,7 @@ use crate::{ error::FinalError, info, list::FileInArchive, + progress::OutputLine, utils::{self, Bytes, FileVisibilityPolicy}, }; @@ -22,7 +23,7 @@ use crate::{ pub fn unpack_archive( reader: Box, output_folder: &Path, - mut log_out: impl Write, + mut log_out: impl OutputLine, ) -> crate::Result> { assert!(output_folder.read_dir().expect("dir exists").count() == 0); let mut archive = tar::Archive::new(reader); @@ -90,7 +91,7 @@ pub fn build_archive_from_paths( ) -> crate::Result where W: Write, - D: Write, + D: OutputLine, { let mut builder = tar::Builder::new(writer); diff --git a/src/archive/zip.rs b/src/archive/zip.rs index 9b18318..c804a91 100644 --- a/src/archive/zip.rs +++ b/src/archive/zip.rs @@ -19,6 +19,7 @@ use crate::{ error::FinalError, info, list::FileInArchive, + progress::OutputLine, utils::{ self, cd_into_same_dir_as, get_invalid_utf8_paths, pretty_format_list_of_paths, strip_cur_dir, to_utf, Bytes, FileVisibilityPolicy, @@ -34,7 +35,7 @@ pub fn unpack_archive( ) -> crate::Result> where R: Read + Seek, - D: Write, + D: OutputLine, { assert!(output_folder.read_dir().expect("dir exists").count() == 0); @@ -141,7 +142,7 @@ pub fn build_archive_from_paths( ) -> crate::Result where W: Write + Seek, - D: Write, + D: OutputLine, { let mut writer = zip::ZipWriter::new(writer); let options = zip::write::FileOptions::default(); diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 440b9fc..1054b29 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -89,7 +89,7 @@ pub fn compress_files( } Tar => { if is_running_in_accessible_mode() { - archive::tar::build_archive_from_paths(&files, &mut writer, file_visibility_policy, io::stdout())?; + archive::tar::build_archive_from_paths(&files, &mut writer, file_visibility_policy, io::stderr())?; writer.flush()?; } else { let mut progress = Progress::new(total_input_size, precise, true); @@ -111,7 +111,7 @@ pub fn compress_files( let mut vec_buffer = Cursor::new(vec![]); if is_running_in_accessible_mode() { - archive::zip::build_archive_from_paths(&files, &mut vec_buffer, file_visibility_policy, io::stdout())?; + archive::zip::build_archive_from_paths(&files, &mut vec_buffer, file_visibility_policy, io::stderr())?; vec_buffer.rewind()?; io::copy(&mut vec_buffer, &mut writer)?; } else { diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index bddf7eb..66ec78e 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -1,5 +1,5 @@ use std::{ - io::{self, BufReader, Read, Write}, + io::{self, BufReader, Read}, ops::ControlFlow, path::{Path, PathBuf}, }; @@ -15,7 +15,7 @@ use crate::{ Extension, }, info, - progress::Progress, + progress::{OutputLine, Progress}, utils::{self, nice_directory_display, user_wants_to_continue}, QuestionAction, QuestionPolicy, BUFFER_CAPACITY, }; @@ -183,7 +183,7 @@ pub fn decompress_file( /// output_dir named after the archive (given by `output_file_path`) /// Note: This functions assumes that `output_dir` exists fn smart_unpack( - unpack_fn: impl FnOnce(&Path, &mut dyn Write) -> crate::Result>, + unpack_fn: impl FnOnce(&Path, &mut dyn OutputLine) -> crate::Result>, total_input_size: u64, output_dir: &Path, output_file_path: &Path, @@ -200,7 +200,7 @@ fn smart_unpack( // unpack the files let files = if is_running_in_accessible_mode() { - unpack_fn(temp_dir_path, &mut io::stdout()) + unpack_fn(temp_dir_path, &mut io::stderr()) } else { unpack_fn(temp_dir_path, &mut Progress::new(total_input_size, true, false)) }?; diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 9236a25..b1c3fec 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -5,7 +5,6 @@ mod decompress; mod list; use std::{ - io::Write, ops::ControlFlow, path::{Path, PathBuf}, }; @@ -19,6 +18,7 @@ use crate::{ extension::{self, flatten_compression_formats, Extension, SUPPORTED_EXTENSIONS}, info, list::ListOptions, + progress::OutputLine, utils::{ self, dir_is_empty, pretty_format_list_of_paths, to_utf, try_infer_extension, user_wants_to_continue, FileVisibilityPolicy, diff --git a/src/macros.rs b/src/macros.rs index 40f89e7..8d54d7a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -25,35 +25,26 @@ macro_rules! info { // Accessible (short/important) info message. // Show info message even in ACCESSIBLE mode (accessible, $($arg:tt)*) => { - info!(@::std::io::stdout(), accessible, $($arg)*); + info!(@::std::io::stderr(), accessible, $($arg)*); }; - (@$log_out: expr, accessible, $($arg:tt)*) => { - let log_out = &mut $log_out; + (@$log_out: expr, accessible, $($arg:tt)*) => {{ // if in ACCESSIBLE mode, suppress the "[INFO]" and just print the message if !$crate::accessible::is_running_in_accessible_mode() { - $crate::macros::_info_helper(log_out); + $log_out.output_line_info(format_args!($($arg)*)); + } else { + $log_out.output_line(format_args!($($arg)*)); } - writeln!(log_out, $($arg)*).unwrap(); - }; + }}; // Inccessible (long/no important) info message. // Print info message if ACCESSIBLE is not turned on (inaccessible, $($arg:tt)*) => { - info!(@::std::io::stdout(), inaccessible, $($arg)*); + info!(@::std::io::stderr(), inaccessible, $($arg)*); }; - (@$log_out: expr, inaccessible, $($arg:tt)*) => { + (@$log_out: expr, inaccessible, $($arg:tt)*) => {{ if !$crate::accessible::is_running_in_accessible_mode() { - let log_out = &mut $log_out; - $crate::macros::_info_helper(log_out); - writeln!(log_out, $($arg)*).unwrap(); + $log_out.output_line_info(format_args!($($arg)*)); } - }; -} - -/// Helper to display "\[INFO\]", colored yellow -pub fn _info_helper(handle: &mut impl std::io::Write) { - use crate::utils::colors::{RESET, YELLOW}; - - write!(handle, "{}[INFO]{} ", *YELLOW, *RESET).unwrap(); + }}; } /// Macro that prints \[WARNING\] messages, wraps [`eprintln`]. diff --git a/src/progress.rs b/src/progress.rs index 5c2a530..5e17c12 100644 --- a/src/progress.rs +++ b/src/progress.rs @@ -1,35 +1,52 @@ //! Module that provides functions to display progress bars for compressing and decompressing files. use std::{ - io::{self, Read, Write}, - mem, + fmt::Arguments, + io::{Read, Stderr, Write}, }; use indicatif::{ProgressBar, ProgressBarIter, ProgressStyle}; +use crate::utils::colors::{RESET, YELLOW}; + /// Draw a ProgressBar using a function that checks periodically for the progress pub struct Progress { bar: ProgressBar, - buf: Vec, } -impl Write for Progress { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.buf.extend(buf); +pub trait OutputLine { + fn output_line(&mut self, args: Arguments); + fn output_line_info(&mut self, args: Arguments); +} - if self.buf.last() == Some(&b'\n') { - self.buf.pop(); - self.flush()?; - } - - Ok(buf.len()) +impl OutputLine for Progress { + fn output_line(&mut self, args: Arguments) { + self.bar.set_message(args.to_string()); } - fn flush(&mut self) -> io::Result<()> { - self.bar.set_message( - String::from_utf8(mem::take(&mut self.buf)) - .map_err(|_| io::Error::new(io::ErrorKind::Other, "Failed to parse buffer content as utf8"))?, - ); - Ok(()) + fn output_line_info(&mut self, args: Arguments) { + self.bar.set_message(format!("{}[INFO]{}{args}", *YELLOW, *RESET)); + } +} + +impl OutputLine for Stderr { + fn output_line(&mut self, args: Arguments) { + self.write_fmt(args).unwrap(); + } + + fn output_line_info(&mut self, args: Arguments) { + write!(self, "{}[INFO]{} {args}", *YELLOW, *RESET).unwrap(); + self.write_fmt(args).unwrap(); + self.write_all(b"\n").unwrap(); + } +} + +impl OutputLine for &mut T { + fn output_line(&mut self, args: Arguments) { + (*self).output_line(args) + } + + fn output_line_info(&mut self, args: Arguments) { + (*self).output_line_info(args); } } @@ -55,7 +72,7 @@ impl Progress { let bar = ProgressBar::new(total_input_size) .with_style(ProgressStyle::with_template(&template).unwrap().progress_chars("#>-")); - Progress { bar, buf: Vec::new() } + Progress { bar } } pub(crate) fn wrap_read(&self, read: R) -> ProgressBarIter { diff --git a/src/utils/fs.rs b/src/utils/fs.rs index ac514f7..241ec97 100644 --- a/src/utils/fs.rs +++ b/src/utils/fs.rs @@ -3,14 +3,14 @@ use std::{ env, fs::ReadDir, - io::{Read, Write}, + io::Read, path::{Path, PathBuf}, }; use fs_err as fs; use super::{to_utf, user_wants_to_overwrite}; -use crate::{extension::Extension, info, QuestionPolicy}; +use crate::{extension::Extension, info, progress::OutputLine, QuestionPolicy}; /// Checks if given path points to an empty directory. pub fn dir_is_empty(dir_path: &Path) -> bool {