use std::io::{self, IsTerminal, Write};
use std::time::Duration;
use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
fn stdout_is_tty() -> bool {
std::io::stdout().is_terminal()
}
pub struct Reporter {
bar: ProgressBar,
}
impl Reporter {
pub fn bytes(message: &str, total: u64) -> Self {
let bar = if stdout_is_tty() {
let bar = ProgressBar::new(total);
bar.set_style(
ProgressStyle::with_template(
"{msg} {bar:40.cyan/blue} {bytes}/{total_bytes} ({bytes_per_sec}, {eta})",
)
.unwrap()
.progress_chars("=>-"),
);
bar.set_message(message.to_string());
bar.set_draw_target(ProgressDrawTarget::stdout());
bar
} else {
ProgressBar::hidden()
};
Self { bar }
}
pub fn spinner(message: &str) -> Self {
let bar = if stdout_is_tty() {
let bar = ProgressBar::new_spinner();
bar.set_style(ProgressStyle::with_template("{spinner} {msg}").unwrap());
bar.set_message(message.to_string());
bar.set_draw_target(ProgressDrawTarget::stdout());
bar.enable_steady_tick(Duration::from_millis(100));
bar
} else {
ProgressBar::hidden()
};
Self { bar }
}
pub fn inc(&self, n: u64) {
self.bar.inc(n);
}
pub fn set_message(&self, message: &str) {
self.bar.set_message(message.to_string());
}
pub fn finish(&self) {
self.bar.finish();
}
}
pub struct ProgressWrite<W> {
inner: W,
reporter: Reporter,
}
impl<W> ProgressWrite<W> {
pub fn new(inner: W, reporter: Reporter) -> Self {
Self { inner, reporter }
}
pub fn finish(&self) {
self.reporter.finish();
}
}
impl<W: Write> Write for ProgressWrite<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let n = self.inner.write(buf)?;
self.reporter.inc(n as u64);
Ok(n)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}