use colored::Colorize;
use indicatif::{ProgressBar, ProgressStyle};
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use std::time::Duration;
pub struct LiveTimer {
bar: ProgressBar,
message: String,
stop_flag: Arc<AtomicBool>,
thread: Option<std::thread::JoinHandle<()>>,
}
impl LiveTimer {
pub fn new(message: &str) -> Self {
let bar = ProgressBar::new_spinner();
bar.set_style(
ProgressStyle::default_spinner()
.tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"])
.template(" {spinner:.cyan} {msg}")
.unwrap_or_else(|_| ProgressStyle::default_spinner()),
);
let stop_flag = Arc::new(AtomicBool::new(false));
let stop_clone = Arc::clone(&stop_flag);
let bar_clone = bar.clone();
let label = message.to_string();
let thread = std::thread::spawn(move || {
while !stop_clone.load(Ordering::Relaxed) {
let elapsed = bar_clone.elapsed();
let time_str = format!("{:.3}s", elapsed.as_secs_f64());
bar_clone.set_message(format!("{:<35} {}", label, time_str.dimmed()));
std::thread::sleep(Duration::from_millis(50));
}
});
bar.enable_steady_tick(Duration::from_millis(100));
Self {
bar,
message: message.to_string(),
stop_flag,
thread: Some(thread),
}
}
pub fn maybe_new(enabled: bool, message: &str) -> Option<Self> {
if enabled {
Some(Self::new(message))
} else {
None
}
}
fn stop_thread(&mut self) {
self.stop_flag.store(true, Ordering::Relaxed);
if let Some(t) = self.thread.take() {
let _ = t.join();
}
}
pub fn finish_ok(mut self, suffix: &str) {
let elapsed = self.bar.elapsed();
self.stop_thread();
self.bar.finish_and_clear();
let time_str = format!("{:.3}s", elapsed.as_secs_f64());
println!(
" {} {:<35} {} {}",
"✓".green(),
self.message,
suffix.green(),
time_str.dimmed()
);
}
pub fn finish_timed(mut self) {
let elapsed = self.bar.elapsed();
self.stop_thread();
self.bar.finish_and_clear();
let time_str = format!("{:.3}s", elapsed.as_secs_f64());
println!(
" {} {:<35} {}",
"✓".green(),
self.message,
time_str.dimmed()
);
}
pub fn finish_skipped(mut self, reason: &str) {
self.stop_thread();
self.bar.finish_and_clear();
println!(
" {} {:<35} {}",
"○".dimmed(),
self.message,
reason.dimmed()
);
}
pub fn finish_warn(mut self, suffix: &str) {
self.stop_thread();
self.bar.finish_and_clear();
println!(
" {} {:<35} {}",
"⚠".yellow(),
self.message,
suffix.yellow()
);
}
pub fn finish_err(mut self, suffix: &str) {
self.stop_thread();
self.bar.finish_and_clear();
println!(" {} {}", self.message, suffix.red());
}
pub fn maybe_finish_ok(timer: Option<Self>, suffix: &str) {
if let Some(t) = timer {
t.finish_ok(suffix);
}
}
pub fn maybe_finish_timed(timer: Option<Self>) {
if let Some(t) = timer {
t.finish_timed();
}
}
pub fn maybe_finish_skipped(timer: Option<Self>, reason: &str) {
if let Some(t) = timer {
t.finish_skipped(reason);
}
}
pub fn maybe_finish_warn(timer: Option<Self>, suffix: &str) {
if let Some(t) = timer {
t.finish_warn(suffix);
}
}
pub fn maybe_finish_err(timer: Option<Self>, suffix: &str) {
if let Some(t) = timer {
t.finish_err(suffix);
}
}
}