use colored::Colorize;
use spinners::{Spinner, Spinners};
use std::time::Instant;
pub struct Progress;
impl Progress {
pub fn new(prompt: &str) -> Box<dyn ProgressInterface> {
if log::max_level() == log::LevelFilter::Off {
Box::new(SilentProgressImpl)
} else {
Box::new(VisibleProgressImpl::new(prompt))
}
}
}
pub trait ProgressInterface {
fn success(&mut self);
fn fail(&mut self);
}
struct VisibleProgressImpl {
prompt: String,
start_time: Instant,
spinner: Spinner,
}
impl VisibleProgressImpl {
fn new(prompt: &str) -> Self {
let now = Instant::now();
let initial_prompt = format!("{}...", prompt).bold().to_string();
Self {
prompt: prompt.into(),
start_time: now,
spinner: Spinner::new(Spinners::Dots, initial_prompt),
}
}
}
impl ProgressInterface for VisibleProgressImpl {
fn success(&mut self) {
self.spinner.stop_with_message(format!(
"{} {} (took {:.2}ms)",
"✔".green(),
self.prompt,
self.start_time.elapsed().as_micros() / 1000
));
}
fn fail(&mut self) {
let prompt = self.prompt.bold();
self.spinner.stop_with_message(format!(
"{} {} (took {:.2}ms)",
"✘".red(),
prompt,
self.start_time.elapsed().as_micros() / 1000
));
}
}
struct SilentProgressImpl;
impl ProgressInterface for SilentProgressImpl {
fn success(&mut self) {}
fn fail(&mut self) {}
}
#[macro_export]
macro_rules! progress {
($prompt:expr, $expr:expr) => {{
let mut progress = $crate::internal::progress::Progress::new($prompt);
let result = $expr;
match &result {
Ok(_) => progress.success(),
Err(_) => progress.fail(),
}
result
}};
}