The ProgressBar and Spinner structs have been refactored to use the same patterns as the input module. This should make the API more cohesive and able to support a wider variety of contexts.
DGHQ46S3S2RQ27CXF4OES5GDXUI63VTSYYQFLXHG7PRKSVWB3SZAC use std::time::Duration;use indicatif::MultiProgress;use lazy_static::lazy_static;use indicatif::ProgressStyle;lazy_static! {static ref MULTI_PROGRESS: MultiProgress = MultiProgress::new();}pub const DOWNLOAD_MESSAGE: &str = "Downloading changes";pub const APPLY_MESSAGE: &str = "Applying changes";pub const UPLOAD_MESSAGE: &str = "Uploading changes";pub const COMPLETE_MESSAGE: &str = "Completing changes";pub const OUTPUT_MESSAGE: &str = "Outputting repository";#[derive(Clone, Debug)]pub struct ProgressBar(Option<indicatif::ProgressBar>);impl Drop for ProgressBar {fn drop(&mut self) {// Make sure the progress bar doesn't disappear after completionif let Some(progress_bar) = &self.0 {progress_bar.finish();}}}impl ProgressBar {pub fn new(len: u64, message: &str) -> Result<Self, anyhow::Error> {let style =ProgressStyle::with_template("{msg:<20} [{bar:50}] {pos}/{len} [{elapsed_precise}]")?.progress_chars("=> ");let progress_bar = indicatif::ProgressBar::new(len).with_style(style).with_message(message.to_owned());MULTI_PROGRESS.add(progress_bar.clone());progress_bar.enable_steady_tick(Duration::from_millis(15));Ok(Self(Some(progress_bar)))}pub fn hidden() -> Self {Self(None)}pub fn inc(&self, delta: u64) {if let Some(progress) = self.0.clone() {progress.inc(delta);}}pub fn inner(&self) -> Option<indicatif::ProgressBar> {self.0.clone()}}#[derive(Clone, Debug)]pub struct Spinner(indicatif::ProgressBar);impl Drop for Spinner {fn drop(&mut self) {// Make sure the spinner doesn't disappear after completionself.finish().unwrap();}}impl Spinner {pub fn new(message: &str) -> Result<Self, anyhow::Error> {let style = ProgressStyle::with_template("{msg}{spinner}")?.tick_strings(&[". ", ".. ", "...", " "]);let spinner = indicatif::ProgressBar::new_spinner().with_style(style).with_message(message.to_owned());spinner.enable_steady_tick(Duration::from_millis(200));MULTI_PROGRESS.add(spinner.clone());Ok(Self(spinner))}pub fn finish(&self) -> Result<(), anyhow::Error> {self.0.set_style(ProgressStyle::with_template("{msg}")?);self.0.finish_with_message(format!("{}... done!", self.0.message()));Ok(())}}
use std::sync::Arc;use std::time::Duration;use super::{ProgressBarTrait, SpinnerTrait};use indicatif::{MultiProgress, ProgressBar, ProgressStyle};use lazy_static::lazy_static;lazy_static! {static ref MULTI_PROGRESS: MultiProgress = MultiProgress::new();}pub fn new_progress(len: u64, message: String) -> Arc<ProgressBar> {let style =ProgressStyle::with_template("{msg:<20} [{bar:50}] {pos}/{len} [{elapsed_precise}]").unwrap().progress_chars("=> ");let progress_bar = ProgressBar::new(len).with_style(style).with_message(message);MULTI_PROGRESS.add(progress_bar.clone());progress_bar.enable_steady_tick(Duration::from_millis(15));Arc::new(progress_bar)}impl ProgressBarTrait for Arc<ProgressBar> {fn inc(&self, delta: u64) {self.as_ref().inc(delta);}fn finish(&self) {// Only finish the progress bar if it's the last referenceif Arc::strong_count(self) == 1 {self.as_ref().finish();}}fn boxed_clone(&self) -> Box<(dyn ProgressBarTrait)> {Box::new(self.clone())}}pub fn new_spinner(message: String) -> Arc<ProgressBar> {let style = ProgressStyle::with_template("{msg}{spinner}").unwrap().tick_strings(&[". ", ".. ", "...", " "]);let spinner = ProgressBar::new_spinner().with_style(style).with_message(message);spinner.enable_steady_tick(Duration::from_millis(200));MULTI_PROGRESS.add(spinner.clone());Arc::new(spinner)}impl SpinnerTrait for Arc<ProgressBar> {fn finish(&self) {// Only display finish message if it's the last referenceif Arc::strong_count(self) == 1 {self.set_style(ProgressStyle::with_template("{msg}").unwrap());self.finish_with_message(format!("{}... done!", self.message()));}}fn boxed_clone(&self) -> Box<dyn SpinnerTrait> {Box::new(self.clone())}}
mod terminal;use super::{ProgressBar, Spinner};use crate::{InteractionError, InteractiveContext};pub trait ProgressBarTrait: Send {fn inc(&self, delta: u64);fn finish(&self);fn boxed_clone(&self) -> Box<dyn ProgressBarTrait>;}impl ProgressBar {pub fn new<S: ToString>(len: u64, message: S) -> Result<ProgressBar, InteractionError> {Ok(Self(match crate::get_context()? {InteractiveContext::Terminal | InteractiveContext::NotInteractive => {Box::new(terminal::new_progress(len, message.to_string()))}}))}pub fn inc(&self, delta: u64) {self.0.inc(delta);}fn finish(&self) {self.0.finish()}}impl Drop for ProgressBar {fn drop(&mut self) {self.finish();}}impl Clone for ProgressBar {fn clone(&self) -> Self {Self(self.0.boxed_clone())}}pub trait SpinnerTrait: Send {fn finish(&self);fn boxed_clone(&self) -> Box<dyn SpinnerTrait>;}impl Spinner {pub fn new<S: ToString>(message: S) -> Result<Spinner, InteractionError> {Ok(Self(match crate::get_context()? {InteractiveContext::Terminal | InteractiveContext::NotInteractive => {Box::new(terminal::new_spinner(message.to_string()))}}))}fn finish(&self) {self.0.finish();}}impl Drop for Spinner {fn drop(&mut self) {self.finish();}}impl Clone for Spinner {fn clone(&self) -> Self {Self(self.0.boxed_clone())}}
// TODO: these should be replaced with a more sophisticated localization systempub const DOWNLOAD_MESSAGE: &str = "Downloading changes";pub const APPLY_MESSAGE: &str = "Applying changes";pub const UPLOAD_MESSAGE: &str = "Uploading changes";pub const COMPLETE_MESSAGE: &str = "Completing changes";pub const OUTPUT_MESSAGE: &str = "Outputting repository";
/// A progress bar that is controlled by codepub struct ProgressBar(Box<dyn ProgressBarTrait>);/// An animated progress bar to indicate activitypub struct Spinner(Box<dyn SpinnerTrait>);