use std::sync::Arc;
use std::time::Duration;
use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressStyle};
#[derive(Clone)]
pub struct Progress {
multi: Arc<MultiProgress>,
spinner_style: ProgressStyle,
}
impl Default for Progress {
fn default() -> Self {
Self::new()
}
}
impl Progress {
pub fn new() -> Self {
let multi = MultiProgress::with_draw_target(ProgressDrawTarget::stderr());
let spinner_style = ProgressStyle::with_template("{spinner:.green} {msg}")
.unwrap_or_else(|_| ProgressStyle::default_spinner())
.tick_strings(&["-", "\\", "|", "/"]);
Self {
multi: Arc::new(multi),
spinner_style,
}
}
pub fn task(&self, label: impl Into<String>) -> Task {
let label = label.into();
let bar = self.multi.add(ProgressBar::new_spinner());
bar.set_style(self.spinner_style.clone());
bar.set_message(label.clone());
bar.enable_steady_tick(Duration::from_millis(80));
Task {
bar,
label,
finished: false,
}
}
pub fn println(&self, message: impl AsRef<str>) {
let message = message.as_ref();
let _ = self.multi.println(message);
}
pub fn suspend<F, T>(&self, operation: F) -> T
where
F: FnOnce() -> T,
{
self.multi.suspend(operation)
}
}
pub struct Task {
bar: ProgressBar,
label: String,
finished: bool,
}
impl Task {
pub fn finish_with_message(mut self, message: impl Into<String>) {
self.finished = true;
self.bar.finish_with_message(message.into());
}
pub fn fail(mut self, message: impl Into<String>) {
self.finished = true;
self.bar.abandon_with_message(message.into());
}
pub fn progress_bar(&self) -> ProgressBar {
self.bar.clone()
}
}
impl Drop for Task {
fn drop(&mut self) {
if !self.finished {
self.bar
.abandon_with_message(format!("{} (cancelled)", self.label));
}
}
}