use std::io::IsTerminal;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use indicatif::{ProgressBar, ProgressStyle};
static SUPPRESS_ANIMATION: AtomicBool = AtomicBool::new(false);
pub(crate) fn configure(quiet: bool, no_progress: bool) {
if quiet || no_progress {
SUPPRESS_ANIMATION.store(true, Ordering::Relaxed);
}
}
fn animation_allowed() -> bool {
if SUPPRESS_ANIMATION.load(Ordering::Relaxed) {
return false;
}
if std::env::var_os("CI").is_some() {
return false;
}
if std::env::var_os("NO_COLOR").is_some() {
return false;
}
std::io::stderr().is_terminal()
}
pub(crate) struct Step {
spinner: Option<ProgressBar>,
}
impl Step {
pub(crate) fn start(label: impl AsRef<str>) -> Self {
let label = label.as_ref();
let spinner = if animation_allowed() {
let pb = ProgressBar::new_spinner();
pb.set_style(
ProgressStyle::with_template("{spinner:.cyan} {msg}")
.expect("static template")
.tick_strings(&["|", "/", "-", "\\"]),
);
pb.set_message(format!("{label}…"));
pb.enable_steady_tick(Duration::from_millis(80));
Some(pb)
} else {
None
};
Self { spinner }
}
pub(crate) fn done_with(self, summary: impl AsRef<str>) {
self.clear_spinner();
println!("✓ {}", summary.as_ref());
}
pub(crate) fn failed_with(self, summary: impl AsRef<str>) {
self.clear_spinner();
eprintln!("✗ {}", summary.as_ref());
}
pub(crate) fn clear(self) {
self.clear_spinner();
}
fn clear_spinner(&self) {
if let Some(pb) = &self.spinner {
pb.finish_and_clear();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn start_then_clear_is_safe_in_non_tty_mode() {
let step = Step::start("test");
step.clear();
}
#[test]
fn start_then_done_with_in_non_tty_does_not_panic() {
let step = Step::start("inner step");
step.done_with("inner step ok");
}
#[test]
fn start_then_failed_with_in_non_tty_does_not_panic() {
let step = Step::start("inner step");
step.failed_with("inner step broke");
}
#[test]
fn configure_short_circuits_animation() {
configure(true, false);
assert!(!animation_allowed());
configure(false, true);
assert!(!animation_allowed());
}
}