From 82c7a48cd30d358723997c61d35786c97ede9bd0 Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Tue, 22 Oct 2024 09:26:54 +1100 Subject: [PATCH] make progress bar optional in non-terminal envs --- zkstack_cli/crates/common/src/term/spinner.rs | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/zkstack_cli/crates/common/src/term/spinner.rs b/zkstack_cli/crates/common/src/term/spinner.rs index b97ba075ac4..3ec2631804a 100644 --- a/zkstack_cli/crates/common/src/term/spinner.rs +++ b/zkstack_cli/crates/common/src/term/spinner.rs @@ -1,34 +1,40 @@ -use std::time::Instant; +use std::{fmt::Display, io::IsTerminal, time::Instant}; use cliclack::{spinner, ProgressBar}; -use crate::config::global_config; +use crate::{config::global_config, logger}; /// Spinner is a helper struct to show a spinner while some operation is running. pub struct Spinner { msg: String, - pb: ProgressBar, + output: SpinnerOutput, time: Instant, } impl Spinner { /// Create a new spinner with a message. pub fn new(msg: &str) -> Self { - let pb = spinner(); - pb.start(msg); - if global_config().verbose { - pb.stop(msg); - } + let output = if std::io::stdout().is_terminal() { + let pb = spinner(); + pb.start(msg); + if global_config().verbose { + pb.stop(msg); + } + SpinnerOutput::Progress(pb) + } else { + logger::info(msg); + SpinnerOutput::Plain() + }; Spinner { msg: msg.to_owned(), - pb, + output, time: Instant::now(), } } /// Manually finish the spinner. pub fn finish(self) { - self.pb.stop(format!( + self.output.stop(format!( "{} done in {} secs", self.msg, self.time.elapsed().as_secs_f64() @@ -37,7 +43,7 @@ impl Spinner { /// Interrupt the spinner with a failed message. pub fn fail(self) { - self.pb.error(format!( + self.output.error(format!( "{} failed in {} secs", self.msg, self.time.elapsed().as_secs_f64() @@ -46,6 +52,33 @@ impl Spinner { /// Freeze the spinner with current message. pub fn freeze(self) { - self.pb.stop(self.msg); + self.output.stop(self.msg); + } +} + +/// An abstraction that makes interactive progress bar optional in environments where virtual +/// terminal is not available. +/// +/// Uses plain `logger::{info,error}` as the fallback. +/// +/// See https://github.com/console-rs/indicatif/issues/530 for more details. +enum SpinnerOutput { + Progress(ProgressBar), + Plain(), +} + +impl SpinnerOutput { + fn error(&self, msg: impl Display) { + match self { + SpinnerOutput::Progress(pb) => pb.error(msg), + SpinnerOutput::Plain() => logger::error(msg), + } + } + + fn stop(self, msg: impl Display) { + match self { + SpinnerOutput::Progress(pb) => pb.stop(msg), + SpinnerOutput::Plain() => logger::info(msg), + } } }