···11+use indicatif::{HumanBytes, ProgressBar};
22+33+/// Progress bar for the compress process
44+pub struct Progress {
55+ /// The progress bar
66+ bar: ProgressBar,
77+ /// The number of bytes read from the input
88+ input_read: u64,
99+ /// The number of bytes written to the output
1010+ output_written: u64,
1111+}
1212+1313+impl Progress {
1414+ /// Create a new progress bar
1515+ pub fn new(input_size: Option<u64>) -> Self {
1616+ let bar = match input_size {
1717+ Some(size) => ProgressBar::new(size),
1818+ None => ProgressBar::new_spinner(),
1919+ };
2020+ bar.set_style(
2121+ indicatif::ProgressStyle::default_bar()
2222+ .template("{spinner:.green} [{elapsed_precise}] ({eta}) [{bar:40.cyan/blue}] {bytes}/{total_bytes} => {msg}").unwrap()
2323+ .progress_chars("#>-"),
2424+ );
2525+ Progress {
2626+ bar,
2727+ input_read: 0,
2828+ output_written: 0,
2929+ }
3030+ }
3131+3232+ /// Update the progress bar with the number of bytes read from the input
3333+ pub fn update_input(&mut self, bytes_read: u64) {
3434+ self.input_read = bytes_read;
3535+ self.bar.set_position(self.input_read);
3636+ }
3737+3838+ /// Update the progress bar with the number of bytes written to the output
3939+ pub fn update_output(&mut self, bytes_written: u64) {
4040+ self.output_written = bytes_written;
4141+ self.bar
4242+ .set_message(HumanBytes(self.output_written).to_string());
4343+ }
4444+4545+ /// Finish the progress bar
4646+ pub fn finish(&self) {
4747+ self.bar.finish();
4848+ }
4949+}
+61-7
src/xz.rs
···11-use crate::utils::*;
11+use crate::{progress::Progress, utils::*};
22use std::{
33 fs::File,
44 io::{self, Read, Write},
···2727 }
28282929 fn compress(&self, input: CmprssInput, output: CmprssOutput) -> Result<(), io::Error> {
3030+ let mut file_size = None;
3031 let mut input_stream = match input {
3132 CmprssInput::Path(paths) => {
3233 if paths.len() > 1 {
3334 return cmprss_error("only 1 file can be compressed at a time");
3435 }
3535- Box::new(File::open(paths[0].as_path())?)
3636+ let file = Box::new(File::open(paths[0].as_path())?);
3737+ // Get the file size for the progress bar
3838+ if let Ok(metadata) = file.metadata() {
3939+ file_size = Some(metadata.len());
4040+ }
4141+ file
3642 }
3743 CmprssInput::Pipe(pipe) => Box::new(pipe) as Box<dyn Read + Send>,
3844 };
4545+ // We want to use the progress bar unless this is in the middle of a pipe
4646+ let mut progress_bar = false;
3947 let output_stream: Box<dyn Write + Send> = match output {
4040- CmprssOutput::Path(path) => Box::new(File::create(path)?),
4848+ CmprssOutput::Path(path) => {
4949+ progress_bar = true;
5050+ Box::new(File::create(path)?)
5151+ }
4152 CmprssOutput::Pipe(pipe) => Box::new(pipe) as Box<dyn Write + Send>,
4253 };
4354 let mut encoder = XzEncoder::new(output_stream, self.level);
4444- io::copy(&mut input_stream, &mut encoder)?;
5555+ if progress_bar {
5656+ let mut progress = Progress::new(file_size);
5757+ // Copy the input to the output in 8k chunks
5858+ let mut buffer = [0; 8192];
5959+ loop {
6060+ let bytes_read = input_stream.read(&mut buffer)?;
6161+ if bytes_read == 0 {
6262+ break;
6363+ }
6464+ encoder.write_all(&buffer[..bytes_read])?;
6565+ progress.update_input(encoder.total_in());
6666+ progress.update_output(encoder.total_out());
6767+ }
6868+ progress.finish();
6969+ } else {
7070+ io::copy(&mut input_stream, &mut encoder)?;
7171+ }
4572 Ok(())
4673 }
47744875 fn extract(&self, input: CmprssInput, output: CmprssOutput) -> Result<(), io::Error> {
7676+ let mut file_size = None;
4977 let mut input_stream = match input {
5078 CmprssInput::Path(paths) => {
5179 if paths.len() > 1 {
5280 return cmprss_error("only 1 file can be extracted at a time");
5381 }
5454- Box::new(File::open(paths[0].as_path())?)
8282+ let file = Box::new(File::open(paths[0].as_path())?);
8383+ // Get the file size for the progress bar
8484+ if let Ok(metadata) = file.metadata() {
8585+ file_size = Some(metadata.len());
8686+ }
8787+ file
5588 }
5689 CmprssInput::Pipe(pipe) => Box::new(pipe) as Box<dyn Read + Send>,
5790 };
9191+ // We want to use the progress bar unless this is in the middle of a pipe
9292+ let mut progress_bar = false;
5893 let output_stream: Box<dyn Write + Send> = match output {
5959- CmprssOutput::Path(path) => Box::new(File::create(path)?),
9494+ CmprssOutput::Path(path) => {
9595+ progress_bar = true;
9696+ Box::new(File::create(path)?)
9797+ }
6098 CmprssOutput::Pipe(pipe) => Box::new(pipe) as Box<dyn Write + Send>,
6199 };
62100 let mut decoder = XzDecoder::new(output_stream);
6363- io::copy(&mut input_stream, &mut decoder)?;
101101+ if progress_bar {
102102+ let mut progress = Progress::new(file_size);
103103+ // Copy the input to the output in 8k chunks
104104+ let mut buffer = [0; 8192];
105105+ loop {
106106+ let bytes_read = input_stream.read(&mut buffer)?;
107107+ if bytes_read == 0 {
108108+ break;
109109+ }
110110+ decoder.write_all(&buffer[..bytes_read])?;
111111+ progress.update_input(decoder.total_in());
112112+ progress.update_output(decoder.total_out());
113113+ }
114114+ progress.finish();
115115+ } else {
116116+ io::copy(&mut input_stream, &mut decoder)?;
117117+ }
64118 Ok(())
65119 }
66120}