summaryrefslogtreecommitdiff
path: root/cli/progress.rs
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2019-05-11 10:23:19 -0400
committerGitHub <noreply@github.com>2019-05-11 10:23:19 -0400
commitaba952397ae668714add770c5b6fa6edf1cf3eb5 (patch)
treea1d2f0bd9758cdd5b196e3ed8efc12c657795aab /cli/progress.rs
parent2c6b93e0a0c7927dc4dd66c1f681a78a6d79b8f1 (diff)
Add progress bar (#2309)
Diffstat (limited to 'cli/progress.rs')
-rw-r--r--cli/progress.rs161
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());
+ }
+}