use indicatif::{ProgressBar, ProgressStyle};
use std::time::Duration;
pub struct CommandSpinner {
bar: Option<ProgressBar>,
}
impl CommandSpinner {
pub fn new(message: &str) -> Self {
let bar = ProgressBar::new_spinner();
bar.set_style(
ProgressStyle::with_template("{spinner:.green} {msg} ({elapsed_precise:.dim})")
.expect("invalid spinner template")
.tick_chars("\u{28CB}\u{2819}\u{2839}\u{2838}\u{283C}\u{2834}\u{2826}\u{2827}\u{2807}\u{280F}"),
);
bar.set_message(message.to_string());
bar.enable_steady_tick(Duration::from_millis(100));
Self { bar: Some(bar) }
}
pub fn new_maybe(message: &str, quiet: bool) -> Self {
if quiet {
Self { bar: None }
} else {
Self::new(message)
}
}
pub fn update(&self, message: &str) {
if let Some(ref bar) = self.bar {
bar.set_message(message.to_string());
}
}
#[allow(dead_code)]
pub fn suspend<F: FnOnce() -> R, R>(&self, f: F) -> R {
if let Some(ref bar) = self.bar {
bar.suspend(f)
} else {
f()
}
}
pub fn success(self, message: &str) {
if let Some(bar) = self.bar {
bar.finish_with_message(format!(
"{} {}",
console::style("\u{2713}").green(),
message
));
}
}
pub fn fail(self, message: &str) {
if let Some(bar) = self.bar {
bar.finish_with_message(format!("{} {}", console::style("\u{2717}").red(), message));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn spinner_new_does_not_panic() {
let spinner = CommandSpinner::new("test");
spinner.success("done");
}
#[test]
fn spinner_quiet_mode_is_noop() {
let spinner = CommandSpinner::new_maybe("test", true);
assert!(spinner.bar.is_none());
spinner.update("updated");
}
#[test]
fn spinner_quiet_mode_success_is_noop() {
let spinner = CommandSpinner::new_maybe("test", true);
spinner.success("done");
}
#[test]
fn spinner_quiet_mode_fail_is_noop() {
let spinner = CommandSpinner::new_maybe("test", true);
spinner.fail("failed");
}
#[test]
fn suspend_runs_closure_and_returns_result() {
let spinner = CommandSpinner::new("test");
let result = spinner.suspend(|| 42);
assert_eq!(result, 42);
spinner.success("done");
}
#[test]
fn suspend_quiet_mode_runs_closure_directly() {
let spinner = CommandSpinner::new_maybe("test", true);
let result = spinner.suspend(|| "hello");
assert_eq!(result, "hello");
}
}