diff options
| author | Bartek Iwańczuk <biwanczuk@gmail.com> | 2022-09-09 21:57:39 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-09-09 15:57:39 -0400 |
| commit | f92bd986de7f083ac164c0e3a2b04d3504c29741 (patch) | |
| tree | dbd0a3484014da69ab8258c5f8e86616574741af /cli/progress_bar.rs | |
| parent | 3bce2af0ebc997278519f5d6270915bb3fe8fd56 (diff) | |
feat: download progress bar (#15814)
Diffstat (limited to 'cli/progress_bar.rs')
| -rw-r--r-- | cli/progress_bar.rs | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/cli/progress_bar.rs b/cli/progress_bar.rs new file mode 100644 index 000000000..f52d137c2 --- /dev/null +++ b/cli/progress_bar.rs @@ -0,0 +1,132 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use crate::colors; +use deno_core::parking_lot::Mutex; +use indexmap::IndexSet; +use std::sync::Arc; +use std::time::Duration; + +#[derive(Clone, Debug, Default)] +pub struct ProgressBar(Arc<Mutex<ProgressBarInner>>); + +#[derive(Debug)] +struct ProgressBarInner { + pb: Option<indicatif::ProgressBar>, + is_tty: bool, + in_flight: IndexSet<String>, +} + +impl Default for ProgressBarInner { + fn default() -> Self { + Self { + pb: None, + is_tty: colors::is_tty(), + in_flight: IndexSet::default(), + } + } +} + +impl ProgressBarInner { + fn get_or_create_pb(&mut self) -> indicatif::ProgressBar { + if let Some(pb) = self.pb.as_ref() { + return pb.clone(); + } + + let pb = indicatif::ProgressBar::new_spinner(); + pb.enable_steady_tick(Duration::from_millis(120)); + pb.set_prefix("Download"); + pb.set_style( + indicatif::ProgressStyle::with_template( + "{prefix:.green} {spinner:.green} {msg}", + ) + .unwrap() + .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]), + ); + self.pb = Some(pb); + self.pb.as_ref().unwrap().clone() + } + + fn add_in_flight(&mut self, msg: &str) { + if self.in_flight.contains(msg) { + return; + } + + self.in_flight.insert(msg.to_string()); + } + + /// Returns if removed "in-flight" was last entry and progress + /// bar needs to be updated. + fn remove_in_flight(&mut self, msg: &str) -> bool { + if !self.in_flight.contains(msg) { + return false; + } + + let mut is_last = false; + if let Some(last) = self.in_flight.last() { + is_last = last == msg; + } + self.in_flight.remove(msg); + is_last + } + + fn update_progress_bar(&mut self) { + let pb = self.get_or_create_pb(); + if let Some(msg) = self.in_flight.last() { + pb.set_message(msg.clone()); + } + } +} + +pub struct UpdateGuard { + pb: ProgressBar, + msg: String, + noop: bool, +} + +impl Drop for UpdateGuard { + fn drop(&mut self) { + if self.noop { + return; + } + + let mut inner = self.pb.0.lock(); + if inner.remove_in_flight(&self.msg) { + inner.update_progress_bar(); + } + } +} + +impl ProgressBar { + pub fn update(&self, msg: &str) -> UpdateGuard { + let mut guard = UpdateGuard { + pb: self.clone(), + msg: msg.to_string(), + noop: false, + }; + let mut inner = self.0.lock(); + + // If we're not running in TTY we're just gonna fallback + // to using logger crate. + if !inner.is_tty { + log::log!(log::Level::Info, "{} {}", colors::green("Download"), msg); + guard.noop = true; + return guard; + } + + inner.add_in_flight(msg); + inner.update_progress_bar(); + guard + } + + pub fn clear(&self) { + let mut inner = self.0.lock(); + + match inner.pb.as_ref() { + Some(pb) => { + pb.finish_and_clear(); + inner.pb = None; + } + None => {} + }; + } +} |
