use std::io::{self, BufRead, Read};
use kdam::{BarExt, Column, RichProgress, Spinner, tqdm};
use std::io::{Error, ErrorKind, Result};
pub struct ProgressReader<R: Read> {
inner: R,
pb: RichProgress,
total: Option<usize>,
}
impl<R: Read> ProgressReader<R> {
pub fn new(inner: R, pb: RichProgress, total: Option<usize>) -> Self {
Self { inner, pb, total }
}
}
impl<R: Read> Read for ProgressReader<R> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let bytes_read = self.inner.read(buf)?;
if bytes_read > 0 {
self.pb
.update(bytes_read)
.map_err(|e| Error::new(ErrorKind::Other, e))?;
}
Result::Ok(bytes_read)
}
}
impl<R: BufRead> BufRead for ProgressReader<R> {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
self.inner.fill_buf()
}
fn consume(&mut self, amt: usize) {
if amt > 0 {
let _ = self.pb.update(amt);
}
self.inner.consume(amt);
}
}
impl<R: Read> Drop for ProgressReader<R> {
fn drop(&mut self) {
if let Some(total) = self.total {
let _ = self.pb.update_to(total);
}
}
}
pub trait ProgressBar {
fn progress(
self,
lable: impl Into<String>,
unit: impl Into<String>,
) -> RichProgress;
}
impl ProgressBar for usize {
fn progress(
self,
lable: impl Into<String>,
unit: impl Into<String>,
) -> RichProgress {
RichProgress::new(
tqdm!(
total = self,
unit_scale = true,
unit_divisor = 1024,
unit = unit.into()
),
vec![
Column::Spinner(Spinner::new(
&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
80.0,
1.0,
)),
Column::Text(
format!("[bold blue] {} :: ", lable.into()).to_owned(),
),
Column::Animation,
Column::Percentage(1),
Column::Text("•".to_owned()),
Column::CountTotal,
Column::Text("•".to_owned()),
Column::Rate,
Column::Text("•".to_owned()),
Column::RemainingTime,
],
)
}
}