Skip to content

Commit

Permalink
Improve UX
Browse files Browse the repository at this point in the history
  • Loading branch information
sigmaSd committed Nov 17, 2021
1 parent cc14cce commit 83ec4bf
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 20 deletions.
6 changes: 4 additions & 2 deletions src/archive/tar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@ pub fn list_archive(reader: Box<dyn Read>) -> crate::Result<Vec<FileInArchive>>
}

/// Compresses the archives given by `input_filenames` into the file given previously to `writer`.
pub fn build_archive_from_paths<W>(input_filenames: &[PathBuf], writer: W) -> crate::Result<W>
pub fn build_archive_from_paths<W, D>(input_filenames: &[PathBuf], writer: W, mut display_handle: D) -> crate::Result<W>
where
W: Write,
D: Write,
{
let mut builder = tar::Builder::new(writer);

Expand All @@ -80,7 +81,8 @@ where
let entry = entry?;
let path = entry.path();

info!("Compressing '{}'.", utils::to_utf(path));
write!(display_handle, "Compressing '{}'.", utils::to_utf(path)).unwrap();
display_handle.flush().unwrap();

if path.is_dir() {
builder.append_dir(path, path)?;
Expand Down
6 changes: 4 additions & 2 deletions src/archive/zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,10 @@ where
}

/// Compresses the archives given by `input_filenames` into the file given previously to `writer`.
pub fn build_archive_from_paths<W>(input_filenames: &[PathBuf], writer: W) -> crate::Result<W>
pub fn build_archive_from_paths<W, D>(input_filenames: &[PathBuf], writer: W, mut display_handle: D) -> crate::Result<W>
where
W: Write + Seek,
D: Write,
{
let mut writer = zip::ZipWriter::new(writer);
let options = zip::write::FileOptions::default();
Expand All @@ -125,7 +126,8 @@ where
let entry = entry?;
let path = entry.path();

info!("Compressing '{}'.", to_utf(path));
write!(display_handle, "Compressing '{}'.", to_utf(path)).unwrap();
display_handle.flush().unwrap();

if path.is_dir() {
if dir_is_empty(path) {
Expand Down
8 changes: 4 additions & 4 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,8 @@ fn compress_files(files: Vec<PathBuf>, formats: Vec<Extension>, output_file: fs:
io::copy(&mut reader, &mut writer)?;
}
Tar => {
let _progress = progress::ProgressByPath::new(total_input_size, precise, output_file_path);
archive::tar::build_archive_from_paths(&files, &mut writer)?;
let mut progress = progress::ProgressByPath::new(total_input_size, precise, output_file_path);
archive::tar::build_archive_from_paths(&files, &mut writer, progress.display_handle())?;
writer.flush()?;
}
Zip => {
Expand All @@ -333,9 +333,9 @@ fn compress_files(files: Vec<PathBuf>, formats: Vec<Extension>, output_file: fs:

let mut vec_buffer = io::Cursor::new(vec![]);
// Safety: &vec_buffer is valid and vec_buffer will remain valid after dropping the progress bar.
let _progress =
let mut progress =
unsafe { progress::ProgressByCursor::new(total_input_size, precise, &vec_buffer as *const _) };
archive::zip::build_archive_from_paths(&files, &mut vec_buffer)?;
archive::zip::build_archive_from_paths(&files, &mut vec_buffer, progress.display_handle())?;
let vec_buffer = vec_buffer.into_inner();
io::copy(&mut vec_buffer.as_slice(), &mut writer)?;
}
Expand Down
60 changes: 48 additions & 12 deletions src/progress.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
//! Module that provides functions to display progress bars for compressing and decompressing files.
use std::{
io::Cursor,
io::{self, Cursor},
path::PathBuf,
sync::mpsc::{self, Sender},
thread,
time::Duration,
};

use indicatif::{ProgressBar, ProgressStyle};

struct DisplayHandle {
buf: Vec<u8>,
sender: Sender<String>,
}
impl io::Write for DisplayHandle {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buf.extend(buf);
Ok(buf.len())
}

fn flush(&mut self) -> io::Result<()> {
self.sender.send(String::from_utf8(self.buf.drain(..).collect()).unwrap()).unwrap();
Ok(())
}
}

/// Draw a ProgressBar using an io::Cursor to check periodically for the progress (for zip archives)
pub struct ProgressByCursor {
draw_stop: Sender<()>,
display_handle: DisplayHandle,
}
impl ProgressByCursor {
/// Create a ProgressBar using an io::Cursor to check periodically for the progress (for zip archives)
Expand All @@ -24,11 +42,12 @@ impl ProgressByCursor {
SendPtr(cursor)
};
let template = if precise {
"{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})"
"{spinner:.green} {prefix} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})"
} else {
"{spinner:.green} [{elapsed}] [{wide_bar:.cyan/blue}] {bytes}/?? ({bytes_per_sec}, {eta}) {path}"
"{spinner:.green} {prefix} [{elapsed}] [{wide_bar:.cyan/blue}] {bytes}/?? ({bytes_per_sec}, {eta}) {path}"
};
let (tx, rx) = mpsc::channel();
let (draw_tx, draw_rx) = mpsc::channel();
let (msg_tx, msg_rx) = mpsc::channel();

thread::spawn(move || {
let cursor = {
Expand All @@ -37,16 +56,23 @@ impl ProgressByCursor {
};
let pb = ProgressBar::new(total_input_size);
pb.set_style(ProgressStyle::default_bar().template(template).progress_chars("#>-"));
while rx.try_recv().is_err() {
thread::sleep(Duration::from_millis(100));
while draw_rx.try_recv().is_err() {
// Safety:
// - The pointer validity is guaranteed by the contract of the `new` function.
// - We don't care if the value is written underneath us (its just an approximation anyway)
pb.set_position(unsafe { &*cursor }.position() as u64);
if let Ok(msg) = msg_rx.try_recv() {
pb.set_prefix(msg);
}
thread::sleep(Duration::from_millis(100));
}
pb.finish();
});
ProgressByCursor { draw_stop: tx }
ProgressByCursor { draw_stop: draw_tx, display_handle: DisplayHandle { buf: Vec::new(), sender: msg_tx } }
}

pub(crate) fn display_handle(&mut self) -> &mut impl io::Write {
&mut self.display_handle
}
}
impl Drop for ProgressByCursor {
Expand All @@ -58,6 +84,7 @@ impl Drop for ProgressByCursor {
/// Draw a ProgressBar using a path to an output file to check periodically for the progress
pub struct ProgressByPath {
draw_stop: Sender<()>,
display_handle: DisplayHandle,
}
impl ProgressByPath {
/// Create a ProgressBar using a path to an output file to check periodically for the progress
Expand All @@ -69,22 +96,31 @@ impl ProgressByPath {
// - canonicalize seems to fix this
let output_file_path = output_file_path.canonicalize().unwrap();

let (tx, rx) = mpsc::channel();
let (draw_tx, draw_rx) = mpsc::channel();
let (msg_tx, msg_rx) = mpsc::channel();

let template = if precise {
"{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})"
"{spinner:.green} {prefix} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})"
} else {
"{spinner:.green} [{elapsed}] [{wide_bar:.cyan/blue}] {bytes}/?? ({bytes_per_sec}, {eta}) {path}"
"{spinner:.green} {prefix} [{elapsed}] [{wide_bar:.cyan/blue}] {bytes}/?? ({bytes_per_sec}, {eta}) {path}"
};
thread::spawn(move || {
let pb = ProgressBar::new(total_input_size);
pb.set_style(ProgressStyle::default_bar().template(template).progress_chars("#>-"));
while rx.try_recv().is_err() {
while draw_rx.try_recv().is_err() {
if let Ok(msg) = msg_rx.try_recv() {
pb.set_prefix(msg);
}
pb.set_position(output_file_path.metadata().unwrap().len());
thread::sleep(Duration::from_millis(100));
}
pb.finish();
});
ProgressByPath { draw_stop: tx }
ProgressByPath { draw_stop: draw_tx, display_handle: DisplayHandle { buf: Vec::new(), sender: msg_tx } }
}

pub(crate) fn display_handle(&mut self) -> &mut impl io::Write {
&mut self.display_handle
}
}
impl Drop for ProgressByPath {
Expand Down

0 comments on commit 83ec4bf

Please sign in to comment.