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 completion
if 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 completion
self.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 reference
if 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 reference
if 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 system
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";
/// A progress bar that is controlled by code
pub struct ProgressBar(Box<dyn ProgressBarTrait>);
/// An animated progress bar to indicate activity
pub struct Spinner(Box<dyn SpinnerTrait>);