diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2019-05-11 10:23:19 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-11 10:23:19 -0400 |
commit | aba952397ae668714add770c5b6fa6edf1cf3eb5 (patch) | |
tree | a1d2f0bd9758cdd5b196e3ed8efc12c657795aab /cli/progress.rs | |
parent | 2c6b93e0a0c7927dc4dd66c1f681a78a6d79b8f1 (diff) |
Add progress bar (#2309)
Diffstat (limited to 'cli/progress.rs')
-rw-r--r-- | cli/progress.rs | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/cli/progress.rs b/cli/progress.rs new file mode 100644 index 000000000..a9bfcc37d --- /dev/null +++ b/cli/progress.rs @@ -0,0 +1,161 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +use std::sync::Arc; +use std::sync::Mutex; + +#[derive(Clone, Default)] +pub struct Progress(Arc<Mutex<Inner>>); + +impl Progress { + pub fn new() -> Self { + Progress::default() + } + + pub fn set_callback<F>(&self, f: F) + where + F: Fn(bool, usize, usize, &str) + Send + Sync + 'static, + { + let mut s = self.0.lock().unwrap(); + assert!(s.callback.is_none()); + s.callback = Some(Arc::new(f)); + } + + /// Returns job counts: (complete, total) + pub fn progress(&self) -> (usize, usize) { + let s = self.0.lock().unwrap(); + s.progress() + } + + pub fn history(&self) -> Vec<String> { + let s = self.0.lock().unwrap(); + s.job_names.clone() + } + + pub fn add(&self, name: String) -> Job { + let mut s = self.0.lock().unwrap(); + let id = s.job_names.len(); + s.maybe_call_callback(false, s.complete, s.job_names.len() + 1, &name); + s.job_names.push(name); + Job { + id, + inner: self.0.clone(), + } + } + + pub fn done(&self) { + let s = self.0.lock().unwrap(); + s.maybe_call_callback(true, s.complete, s.job_names.len(), ""); + } +} + +type Callback = dyn Fn(bool, usize, usize, &str) + Send + Sync; + +#[derive(Default)] +struct Inner { + job_names: Vec<String>, + complete: usize, + callback: Option<Arc<Callback>>, +} + +impl Inner { + pub fn maybe_call_callback( + &self, + done: bool, + complete: usize, + total: usize, + msg: &str, + ) { + if let Some(ref cb) = self.callback { + cb(done, complete, total, msg); + } + } + + /// Returns job counts: (complete, total) + pub fn progress(&self) -> (usize, usize) { + let total = self.job_names.len(); + (self.complete, total) + } +} + +pub struct Job { + inner: Arc<Mutex<Inner>>, + id: usize, +} + +impl Drop for Job { + fn drop(&mut self) { + let mut s = self.inner.lock().unwrap(); + s.complete += 1; + let name = &s.job_names[self.id]; + let (complete, total) = s.progress(); + s.maybe_call_callback(false, complete, total, name); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn progress() { + let p = Progress::new(); + assert_eq!(p.progress(), (0, 0)); + { + let _j1 = p.add("hello".to_string()); + assert_eq!(p.progress(), (0, 1)); + } + assert_eq!(p.progress(), (1, 1)); + { + let _j2 = p.add("hello".to_string()); + assert_eq!(p.progress(), (1, 2)); + } + assert_eq!(p.progress(), (2, 2)); + } + + #[test] + fn history() { + let p = Progress::new(); + let _a = p.add("a".to_string()); + let _b = p.add("b".to_string()); + assert_eq!(p.history(), vec!["a", "b"]); + } + + #[test] + fn callback() { + let callback_history: Arc<Mutex<Vec<(usize, usize, String)>>> = + Arc::new(Mutex::new(Vec::new())); + { + let p = Progress::new(); + let callback_history_ = callback_history.clone(); + + p.set_callback(move |_done, complete, total, msg| { + // println!("callback: {}, {}, {}", complete, total, msg); + let mut h = callback_history_.lock().unwrap(); + h.push((complete, total, String::from(msg))); + }); + { + let _a = p.add("a".to_string()); + let _b = p.add("b".to_string()); + } + let _c = p.add("c".to_string()); + } + + let h = callback_history.lock().unwrap(); + assert_eq!( + h.to_vec(), + vec![ + (0, 1, "a".to_string()), + (0, 2, "b".to_string()), + (1, 2, "b".to_string()), + (2, 2, "a".to_string()), + (2, 3, "c".to_string()), + (3, 3, "c".to_string()), + ] + ); + } + + #[test] + fn thread_safe() { + fn f<S: Send + Sync>(_: S) {} + f(Progress::new()); + } +} |