From d4fc8851ea365ec0ec33484bb92ace0f88abf872 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Fri, 14 Oct 2016 10:40:38 -0400 Subject: [PATCH 1/8] feat: initial implmetation of pb::multibar This commit included initial implemtation of a multibar support for unix like os(windows should be added too) and an example the shows how to use this feature. --- Cargo.toml | 5 +- examples/multi.rs | 64 ++++++++++++ src/lib.rs | 22 +++++ src/multi.rs | 246 ++++++++++++++++++++++++++++++++++++++++++++++ src/pb.rs | 7 -- 5 files changed, 336 insertions(+), 8 deletions(-) create mode 100644 examples/multi.rs create mode 100644 src/multi.rs diff --git a/Cargo.toml b/Cargo.toml index 3462ed1b..e2f857c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,10 @@ keywords = ["cli", "progress", "terminal", "pb"] license = "MIT" [dependencies] -time = "0.1" libc = "0.2.9" +time = "0.1.35" winapi = "0.2" kernel32-sys = "0.2" + +[dev-dependencies] +rand = "*" diff --git a/examples/multi.rs b/examples/multi.rs new file mode 100644 index 00000000..ee9be88b --- /dev/null +++ b/examples/multi.rs @@ -0,0 +1,64 @@ +extern crate rand; +extern crate pbr; +use rand::Rng; +use pbr::MultiBar; +use std::thread; +use std::time::Duration; + +fn main() { + let mut mb = MultiBar::new(); + mb.println("Your Application Header:"); + mb.println(""); + + for i in 1..6 { + let count = 100 * i; + let mut pb = mb.create_bar(count); + pb.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏"); + pb.show_message = true; + thread::spawn(move || { + for _ in 0..count/20 { + for _ in 0..20 { + pb.message("Waiting : "); + thread::sleep(Duration::from_millis(50)); + pb.tick(); + } + for _ in 0..20 { + let n = rand::thread_rng().gen_range(0, 100 * i); + pb.message("Connected: "); + thread::sleep(Duration::from_millis(n)); + pb.inc(); + } + } + for _ in 0..20 { + pb.message("Cleaning :"); + thread::sleep(Duration::from_millis(100)); + pb.tick(); + } + pb.message("Completed! "); + pb.tick(); + pb.finish(); + }); + } + + + mb.println(""); + mb.println("Text lines separate between two sections: "); + mb.println(""); + + for i in 1..4 { + let count = 100 * i; + let mut pb = mb.create_bar(count); + thread::spawn(move || { + for _ in 0..count { + pb.inc(); + let n = rand::thread_rng().gen_range(0, 100 * i); + thread::sleep(Duration::from_millis(n)); + } + pb.finish(); + }); + } + + mb.listen(); + + println!("\nall bars done!\n"); +} diff --git a/src/lib.rs b/src/lib.rs index fa282d7d..986e9ee3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,10 +46,32 @@ //! println!("done!"); //! } //! ``` + +// Macro for writing to the giving writer. +// Used in both pb.rs and multi.rs modules. +// +// # Examples +// +// ``` +// let w = io::stdout(); +// printfl!(w, ""); +// printfl!(w, "\r{}", out); +// +// ``` +macro_rules! printfl { + ($w:expr, $($tt:tt)*) => {{ + $w.write(&format!($($tt)*).as_bytes()).ok().expect("write() fail"); + $w.flush().ok().expect("flush() fail"); + }} +} + +#[macro_use] extern crate time; mod tty; mod pb; +mod multi; pub use pb::{ProgressBar, Units}; +pub use multi::{MultiBar}; use std::io::{Write, Stdout, stdout}; pub struct PbIter diff --git a/src/multi.rs b/src/multi.rs new file mode 100644 index 00000000..9cadb993 --- /dev/null +++ b/src/multi.rs @@ -0,0 +1,246 @@ +use pb::ProgressBar; +use std::str::from_utf8; +use std::io::{Stdout, Result, Write}; +use std::sync::mpsc; +use std::sync::mpsc::{Sender, Receiver}; + +pub struct MultiBar{ + nlines: usize, + + lines: Vec, + + nbars: usize, + + chan: (Sender, Receiver), + + handle: T, +} + +impl MultiBar { + /// Create a new MultiBar with stdout as a writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::thread; + /// use pbr::MultiBar; + /// + /// let mut mb = MultiBar::new(); + /// mb.println("Application header:"); + /// + /// let mut p1 = mb.create_bar(count); + /// let _ = thread::spawn(move || { + /// for _ in 0..count { + /// p1.inc(); + /// thread::sleep(Duration::from_millis(100)); + /// } + /// // notify the multibar that this bar finished. + /// p1.finish(); + /// }); + /// + /// mb.println("add a separator between the two bars"); + /// + /// let mut p2 = mb.create_bar(count * 2); + /// let _ = thread::spawn(move || { + /// for _ in 0..count * 2 { + /// p2.inc(); + /// thread::sleep(Duration::from_millis(100)); + /// } + /// // notify the multibar that this bar finished. + /// p2.finish(); + /// }); + /// + /// // start listen to all bars changes. + /// // this is a blocking operation, until all bars will finish. + /// // to ignore blocking, you can run it in a different thread. + /// mb.listen(); + /// ``` + pub fn new() -> MultiBar { + MultiBar::on(::std::io::stdout()) + } +} + +impl MultiBar { + /// Create a new MultiBar with an arbitrary writer. + /// + /// # Examples + /// + /// ```no_run + /// use pbr::MultiBar; + /// use std::io::stderr; + /// + /// let mut mb = MultiBar::on(stderr()); + /// // ... + /// // see full example in `MultiBar::new` + /// // ... + /// ``` + pub fn on(handle: T) -> MultiBar { + MultiBar { + nlines: 0, + nbars: 0, + lines: Vec::new(), + chan: mpsc::channel(), + handle: handle, + } + } + + /// println used to add text lines between the bars. + /// for example: you could add a header to your application, + /// or text separators between bars. + /// + /// /// Create a new MultiBar with stdout as a writer. + /// + /// # Examples + /// + /// ```no_run + /// use pbr::MultiBar; + /// + /// let mut mb = MultiBar::new(); + /// mb.println("Application header:"); + /// + /// let mut p1 = MultiBar::create_bar(count); + /// // ... + /// + /// mb.println("Text line between bar1 and bar2"); + /// + /// let mut p2 = MultiBar::create_bar(count); + /// // ... + /// + /// mb.println("Text line between bar2 and bar3"); + /// + /// // ... + /// // ... + /// mb.listen(); + /// ``` + pub fn println(&mut self, s: &str) { + self.lines.push(s.to_owned()); + self.nlines+=1; + } + + /// create_bar creates new `ProgressBar` with `Pipe` as the writer. + /// + /// The ordering of the method calls is important. it means that in + /// the first call, you get a progress bar in level 1, in the 2nd call, + /// you get a progress bar in level 2, and so on. + /// + /// ProgressBar that finish its work, must call `finish()` to + /// notify the MultiBar about it. + /// + /// # Examples + /// + /// ```no_run + /// use pbr::MultiBar; + /// + /// let mut mb = MultiBar::new(); + /// + /// // progress bar in level 1 + /// let mut p1 = MultiBar::create_bar(count1); + /// // ... + /// + /// // progress bar in level 2 + /// let mut p2 = MultiBar::create_bar(count2); + /// // ... + /// + /// // progress bar in level 3 + /// let mut p3 = MultiBar::create_bar(count3); + /// + /// // ... + /// mb.listen(); + /// ``` + pub fn create_bar(&mut self, total: u64) -> ProgressBar { + self.println(""); + self.nbars += 1; + let mut p = ProgressBar::on(Pipe { + level: self.nlines - 1, + chan: self.chan.0.clone(), + }, total); + p.add(0); + p + } + + + /// listen start listen to all bars changes. + /// + /// ProgressBar that finish its work, must call `finish()` to + /// notify the MultiBar about it. + /// + /// This is a blocking operation and blocks until all bars will + /// finish. + /// To ignore blocking, you can run it in a different thread. + /// + /// # Examples + /// + /// ```no_run + /// use pbr::MultiBar; + /// + /// let mut mb = MultiBar::new(); + /// + /// // ... + /// // create some bars here + /// // ... + /// + /// thread::spawn(move || { + /// mb.listen(); + /// println!("all bars done!"); + /// }); + /// + /// // ... + /// ``` + pub fn listen(&mut self) { + let mut first = true; + let mut nbars = self.nbars; + while nbars > 0 { + + // receive message + let msg = self.chan.1.recv().unwrap(); + if msg.done { + nbars -= 1; + continue + } + self.lines[msg.level] = msg.string; + + // and draw + let mut out = String::new(); + // TODO: implement tty::move_cursor_up(lines), and use it here + if !first { + out.push_str(&format!("\x1B[{}A", self.nlines)); + } else { + first = false; + } + for l in self.lines.iter() { + out.push_str(&format!("\r{}\n", l)); + } + printfl!(self.handle, "{}", out); + } + } +} + +pub struct Pipe { + level: usize, + chan: Sender, +} + +impl Write for Pipe { + fn write(&mut self, buf: &[u8]) -> Result { + let s = from_utf8(buf).unwrap().to_owned(); + self.chan.send(WriteMsg{ + // finish method emit empty string + done: s == "", + level: self.level, + string: s, + }).unwrap(); + Ok(1) + } + + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +// WriteMsg is the message format used to communicate +// between MultiBar and its bars +struct WriteMsg { + done: bool, + level: usize, + string: String, +} diff --git a/src/pb.rs b/src/pb.rs index 0d26cf2e..4bf4b5f6 100644 --- a/src/pb.rs +++ b/src/pb.rs @@ -5,13 +5,6 @@ use time::{self, SteadyTime}; use std::io::Stdout; use tty::{Width, terminal_size}; -macro_rules! printfl { - ($w:expr, $($tt:tt)*) => {{ - $w.write(&format!($($tt)*).as_bytes()).ok().expect("write() fail"); - $w.flush().ok().expect("flush() fail"); - }} -} - macro_rules! kb_fmt { ($n: ident) => {{ let kb = 1024f64; From 612c9a9406d7ed5468f2e8a6393ab2e5c81a6abf Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Fri, 14 Oct 2016 10:40:38 -0400 Subject: [PATCH 2/8] feat: initial implmetation of pb::multibar This commit included initial implemtation of a multibar support for unix like os(windows should be added too) and an example the shows how to use this feature. --- Cargo.toml | 3 + examples/multi.rs | 64 ++++++++++++ src/lib.rs | 22 +++++ src/multi.rs | 246 ++++++++++++++++++++++++++++++++++++++++++++++ src/pb.rs | 7 -- 5 files changed, 335 insertions(+), 7 deletions(-) create mode 100644 examples/multi.rs create mode 100644 src/multi.rs diff --git a/Cargo.toml b/Cargo.toml index 49dfa335..f919325b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,6 @@ time = "0.1.35" libc = "0.2.9" winapi = "0.2" kernel32-sys = "0.2" + +[dev-dependencies] +rand = "*" diff --git a/examples/multi.rs b/examples/multi.rs new file mode 100644 index 00000000..ee9be88b --- /dev/null +++ b/examples/multi.rs @@ -0,0 +1,64 @@ +extern crate rand; +extern crate pbr; +use rand::Rng; +use pbr::MultiBar; +use std::thread; +use std::time::Duration; + +fn main() { + let mut mb = MultiBar::new(); + mb.println("Your Application Header:"); + mb.println(""); + + for i in 1..6 { + let count = 100 * i; + let mut pb = mb.create_bar(count); + pb.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏"); + pb.show_message = true; + thread::spawn(move || { + for _ in 0..count/20 { + for _ in 0..20 { + pb.message("Waiting : "); + thread::sleep(Duration::from_millis(50)); + pb.tick(); + } + for _ in 0..20 { + let n = rand::thread_rng().gen_range(0, 100 * i); + pb.message("Connected: "); + thread::sleep(Duration::from_millis(n)); + pb.inc(); + } + } + for _ in 0..20 { + pb.message("Cleaning :"); + thread::sleep(Duration::from_millis(100)); + pb.tick(); + } + pb.message("Completed! "); + pb.tick(); + pb.finish(); + }); + } + + + mb.println(""); + mb.println("Text lines separate between two sections: "); + mb.println(""); + + for i in 1..4 { + let count = 100 * i; + let mut pb = mb.create_bar(count); + thread::spawn(move || { + for _ in 0..count { + pb.inc(); + let n = rand::thread_rng().gen_range(0, 100 * i); + thread::sleep(Duration::from_millis(n)); + } + pb.finish(); + }); + } + + mb.listen(); + + println!("\nall bars done!\n"); +} diff --git a/src/lib.rs b/src/lib.rs index fa282d7d..986e9ee3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,10 +46,32 @@ //! println!("done!"); //! } //! ``` + +// Macro for writing to the giving writer. +// Used in both pb.rs and multi.rs modules. +// +// # Examples +// +// ``` +// let w = io::stdout(); +// printfl!(w, ""); +// printfl!(w, "\r{}", out); +// +// ``` +macro_rules! printfl { + ($w:expr, $($tt:tt)*) => {{ + $w.write(&format!($($tt)*).as_bytes()).ok().expect("write() fail"); + $w.flush().ok().expect("flush() fail"); + }} +} + +#[macro_use] extern crate time; mod tty; mod pb; +mod multi; pub use pb::{ProgressBar, Units}; +pub use multi::{MultiBar}; use std::io::{Write, Stdout, stdout}; pub struct PbIter diff --git a/src/multi.rs b/src/multi.rs new file mode 100644 index 00000000..9cadb993 --- /dev/null +++ b/src/multi.rs @@ -0,0 +1,246 @@ +use pb::ProgressBar; +use std::str::from_utf8; +use std::io::{Stdout, Result, Write}; +use std::sync::mpsc; +use std::sync::mpsc::{Sender, Receiver}; + +pub struct MultiBar{ + nlines: usize, + + lines: Vec, + + nbars: usize, + + chan: (Sender, Receiver), + + handle: T, +} + +impl MultiBar { + /// Create a new MultiBar with stdout as a writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::thread; + /// use pbr::MultiBar; + /// + /// let mut mb = MultiBar::new(); + /// mb.println("Application header:"); + /// + /// let mut p1 = mb.create_bar(count); + /// let _ = thread::spawn(move || { + /// for _ in 0..count { + /// p1.inc(); + /// thread::sleep(Duration::from_millis(100)); + /// } + /// // notify the multibar that this bar finished. + /// p1.finish(); + /// }); + /// + /// mb.println("add a separator between the two bars"); + /// + /// let mut p2 = mb.create_bar(count * 2); + /// let _ = thread::spawn(move || { + /// for _ in 0..count * 2 { + /// p2.inc(); + /// thread::sleep(Duration::from_millis(100)); + /// } + /// // notify the multibar that this bar finished. + /// p2.finish(); + /// }); + /// + /// // start listen to all bars changes. + /// // this is a blocking operation, until all bars will finish. + /// // to ignore blocking, you can run it in a different thread. + /// mb.listen(); + /// ``` + pub fn new() -> MultiBar { + MultiBar::on(::std::io::stdout()) + } +} + +impl MultiBar { + /// Create a new MultiBar with an arbitrary writer. + /// + /// # Examples + /// + /// ```no_run + /// use pbr::MultiBar; + /// use std::io::stderr; + /// + /// let mut mb = MultiBar::on(stderr()); + /// // ... + /// // see full example in `MultiBar::new` + /// // ... + /// ``` + pub fn on(handle: T) -> MultiBar { + MultiBar { + nlines: 0, + nbars: 0, + lines: Vec::new(), + chan: mpsc::channel(), + handle: handle, + } + } + + /// println used to add text lines between the bars. + /// for example: you could add a header to your application, + /// or text separators between bars. + /// + /// /// Create a new MultiBar with stdout as a writer. + /// + /// # Examples + /// + /// ```no_run + /// use pbr::MultiBar; + /// + /// let mut mb = MultiBar::new(); + /// mb.println("Application header:"); + /// + /// let mut p1 = MultiBar::create_bar(count); + /// // ... + /// + /// mb.println("Text line between bar1 and bar2"); + /// + /// let mut p2 = MultiBar::create_bar(count); + /// // ... + /// + /// mb.println("Text line between bar2 and bar3"); + /// + /// // ... + /// // ... + /// mb.listen(); + /// ``` + pub fn println(&mut self, s: &str) { + self.lines.push(s.to_owned()); + self.nlines+=1; + } + + /// create_bar creates new `ProgressBar` with `Pipe` as the writer. + /// + /// The ordering of the method calls is important. it means that in + /// the first call, you get a progress bar in level 1, in the 2nd call, + /// you get a progress bar in level 2, and so on. + /// + /// ProgressBar that finish its work, must call `finish()` to + /// notify the MultiBar about it. + /// + /// # Examples + /// + /// ```no_run + /// use pbr::MultiBar; + /// + /// let mut mb = MultiBar::new(); + /// + /// // progress bar in level 1 + /// let mut p1 = MultiBar::create_bar(count1); + /// // ... + /// + /// // progress bar in level 2 + /// let mut p2 = MultiBar::create_bar(count2); + /// // ... + /// + /// // progress bar in level 3 + /// let mut p3 = MultiBar::create_bar(count3); + /// + /// // ... + /// mb.listen(); + /// ``` + pub fn create_bar(&mut self, total: u64) -> ProgressBar { + self.println(""); + self.nbars += 1; + let mut p = ProgressBar::on(Pipe { + level: self.nlines - 1, + chan: self.chan.0.clone(), + }, total); + p.add(0); + p + } + + + /// listen start listen to all bars changes. + /// + /// ProgressBar that finish its work, must call `finish()` to + /// notify the MultiBar about it. + /// + /// This is a blocking operation and blocks until all bars will + /// finish. + /// To ignore blocking, you can run it in a different thread. + /// + /// # Examples + /// + /// ```no_run + /// use pbr::MultiBar; + /// + /// let mut mb = MultiBar::new(); + /// + /// // ... + /// // create some bars here + /// // ... + /// + /// thread::spawn(move || { + /// mb.listen(); + /// println!("all bars done!"); + /// }); + /// + /// // ... + /// ``` + pub fn listen(&mut self) { + let mut first = true; + let mut nbars = self.nbars; + while nbars > 0 { + + // receive message + let msg = self.chan.1.recv().unwrap(); + if msg.done { + nbars -= 1; + continue + } + self.lines[msg.level] = msg.string; + + // and draw + let mut out = String::new(); + // TODO: implement tty::move_cursor_up(lines), and use it here + if !first { + out.push_str(&format!("\x1B[{}A", self.nlines)); + } else { + first = false; + } + for l in self.lines.iter() { + out.push_str(&format!("\r{}\n", l)); + } + printfl!(self.handle, "{}", out); + } + } +} + +pub struct Pipe { + level: usize, + chan: Sender, +} + +impl Write for Pipe { + fn write(&mut self, buf: &[u8]) -> Result { + let s = from_utf8(buf).unwrap().to_owned(); + self.chan.send(WriteMsg{ + // finish method emit empty string + done: s == "", + level: self.level, + string: s, + }).unwrap(); + Ok(1) + } + + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +// WriteMsg is the message format used to communicate +// between MultiBar and its bars +struct WriteMsg { + done: bool, + level: usize, + string: String, +} diff --git a/src/pb.rs b/src/pb.rs index 0d26cf2e..4bf4b5f6 100644 --- a/src/pb.rs +++ b/src/pb.rs @@ -5,13 +5,6 @@ use time::{self, SteadyTime}; use std::io::Stdout; use tty::{Width, terminal_size}; -macro_rules! printfl { - ($w:expr, $($tt:tt)*) => {{ - $w.write(&format!($($tt)*).as_bytes()).ok().expect("write() fail"); - $w.flush().ok().expect("flush() fail"); - }} -} - macro_rules! kb_fmt { ($n: ident) => {{ let kb = 1024f64; From a0285b02510c20a20eaeb847e377f23853778456 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Fri, 14 Oct 2016 11:06:04 -0400 Subject: [PATCH 3/8] fmt: run cargo fmt --- src/lib.rs | 21 ++++++++++++--------- src/multi.rs | 27 +++++++++++++++------------ 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 986e9ee3..01a01662 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,19 +71,19 @@ mod tty; mod pb; mod multi; pub use pb::{ProgressBar, Units}; -pub use multi::{MultiBar}; +pub use multi::MultiBar; use std::io::{Write, Stdout, stdout}; pub struct PbIter -where I: Iterator, - T: Write + where I: Iterator, + T: Write { iter: I, progress_bar: ProgressBar, } impl PbIter -where I: Iterator + where I: Iterator { pub fn new(iter: I) -> Self { Self::on(stdout(), iter) @@ -91,18 +91,21 @@ where I: Iterator } impl PbIter -where I: Iterator, - T: Write + where I: Iterator, + T: Write { pub fn on(handle: T, iter: I) -> Self { let size = iter.size_hint().0; - PbIter {iter: iter, progress_bar: ProgressBar::on(handle, size as u64)} + PbIter { + iter: iter, + progress_bar: ProgressBar::on(handle, size as u64), + } } } impl Iterator for PbIter -where I: Iterator, - T: Write + where I: Iterator, + T: Write { type Item = I::Item; diff --git a/src/multi.rs b/src/multi.rs index 9cadb993..2c11922d 100644 --- a/src/multi.rs +++ b/src/multi.rs @@ -4,7 +4,7 @@ use std::io::{Stdout, Result, Write}; use std::sync::mpsc; use std::sync::mpsc::{Sender, Receiver}; -pub struct MultiBar{ +pub struct MultiBar { nlines: usize, lines: Vec, @@ -114,7 +114,7 @@ impl MultiBar { /// ``` pub fn println(&mut self, s: &str) { self.lines.push(s.to_owned()); - self.nlines+=1; + self.nlines += 1; } /// create_bar creates new `ProgressBar` with `Pipe` as the writer. @@ -151,9 +151,10 @@ impl MultiBar { self.println(""); self.nbars += 1; let mut p = ProgressBar::on(Pipe { - level: self.nlines - 1, - chan: self.chan.0.clone(), - }, total); + level: self.nlines - 1, + chan: self.chan.0.clone(), + }, + total); p.add(0); p } @@ -195,7 +196,7 @@ impl MultiBar { let msg = self.chan.1.recv().unwrap(); if msg.done { nbars -= 1; - continue + continue; } self.lines[msg.level] = msg.string; @@ -223,12 +224,14 @@ pub struct Pipe { impl Write for Pipe { fn write(&mut self, buf: &[u8]) -> Result { let s = from_utf8(buf).unwrap().to_owned(); - self.chan.send(WriteMsg{ - // finish method emit empty string - done: s == "", - level: self.level, - string: s, - }).unwrap(); + self.chan + .send(WriteMsg { + // finish method emit empty string + done: s == "", + level: self.level, + string: s, + }) + .unwrap(); Ok(1) } From 90692f9bf112132fa6363c1bda31e87b64282425 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Fri, 14 Oct 2016 11:07:46 -0400 Subject: [PATCH 4/8] fmt: fmt example --- examples/multi.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/multi.rs b/examples/multi.rs index ee9be88b..2ee7af4a 100644 --- a/examples/multi.rs +++ b/examples/multi.rs @@ -16,25 +16,25 @@ fn main() { pb.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏"); pb.show_message = true; thread::spawn(move || { - for _ in 0..count/20 { + for _ in 0..count / 20 { + for _ in 0..20 { + pb.message("Waiting : "); + thread::sleep(Duration::from_millis(50)); + pb.tick(); + } for _ in 0..20 { - pb.message("Waiting : "); - thread::sleep(Duration::from_millis(50)); - pb.tick(); - } - for _ in 0..20 { let n = rand::thread_rng().gen_range(0, 100 * i); - pb.message("Connected: "); - thread::sleep(Duration::from_millis(n)); - pb.inc(); - } + pb.message("Connected: "); + thread::sleep(Duration::from_millis(n)); + pb.inc(); + } + } + for _ in 0..20 { + pb.message("Cleaning :"); + thread::sleep(Duration::from_millis(100)); + pb.tick(); } - for _ in 0..20 { - pb.message("Cleaning :"); - thread::sleep(Duration::from_millis(100)); - pb.tick(); - } - pb.message("Completed! "); + pb.message("Completed! "); pb.tick(); pb.finish(); }); @@ -52,7 +52,7 @@ fn main() { for _ in 0..count { pb.inc(); let n = rand::thread_rng().gen_range(0, 100 * i); - thread::sleep(Duration::from_millis(n)); + thread::sleep(Duration::from_millis(n)); } pb.finish(); }); From 10ce2aa6e98ad77dd0907c59089a886606b55b02 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Mon, 17 Oct 2016 23:09:25 -0400 Subject: [PATCH 5/8] feat(tty/unix): add move_cursor_up method --- src/tty/mod.rs | 8 ++++---- src/tty/unix.rs | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/tty/mod.rs b/src/tty/mod.rs index 1c4ef310..65470201 100644 --- a/src/tty/mod.rs +++ b/src/tty/mod.rs @@ -1,7 +1,7 @@ -//! Most of the code in this module(tty) taken from: +//! Most of the code in for the `terminal_size()` function taken from: //! https://github.com/eminence/terminal-size //! -//! A simple utility for getting the size of a terminal. +//! A simple utility for getting the size of a terminal, and moving `n` lines up. //! //! Supports both Linux and Windows, but help is needed to test other platforms //! @@ -15,9 +15,9 @@ pub struct Height(pub u16); #[cfg(unix)] mod unix; #[cfg(unix)] -pub use self::unix::terminal_size; +pub use self::unix::*; #[cfg(windows)] mod windows; #[cfg(windows)] -pub use self::windows::terminal_size; +pub use self::windows::*; diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 751e8f01..0fdefd16 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -40,6 +40,11 @@ pub fn terminal_size() -> Option<(Width, Height)> { } } +/// Return string that move the cursor `n` lines up. +pub fn move_cursor_up(n: usize) -> String { + format!("\x1B[{}A", n) +} + #[test] /// Compare with the output of `stty size` fn compare_with_stty() { From 8cbeed97b44147a57cd7b76c04553c7ab004fa9f Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Mon, 17 Oct 2016 23:11:01 -0400 Subject: [PATCH 6/8] refac: replace HC with move_cursor method --- src/multi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/multi.rs b/src/multi.rs index 2c11922d..e19f1581 100644 --- a/src/multi.rs +++ b/src/multi.rs @@ -1,5 +1,6 @@ use pb::ProgressBar; use std::str::from_utf8; +use tty::move_cursor_up; use std::io::{Stdout, Result, Write}; use std::sync::mpsc; use std::sync::mpsc::{Sender, Receiver}; @@ -202,9 +203,8 @@ impl MultiBar { // and draw let mut out = String::new(); - // TODO: implement tty::move_cursor_up(lines), and use it here if !first { - out.push_str(&format!("\x1B[{}A", self.nlines)); + out += &move_cursor_up(self.nlines); } else { first = false; } From 4f6e1a8be2c7cae9377ba09f48b8a8e664124f39 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Mon, 17 Oct 2016 23:24:17 -0400 Subject: [PATCH 7/8] feat: poc windows support --- src/tty/windows.rs | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/tty/windows.rs b/src/tty/windows.rs index c340c325..ac11137e 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -8,6 +8,30 @@ use super::{Width, Height}; /// Note that this returns the size of the actual command window, and /// not the overall size of the command window buffer pub fn terminal_size() -> Option<(Width, Height)> { + if let Some((_, csbi)) = get_csbi() { + let w: Width = Width((csbi.srWindow.Right - csbi.srWindow.Left) as u16); + let h: Height = Height((csbi.srWindow.Bottom - csbi.srWindow.Top) as u16); + Some((w, h)) + } else { + None + } +} + +fn move_cursor_up(n: usize) -> String { + use self::kernel32::SetConsoleCursorPosition; + use self::winapi::COORD; + if let Some((hand, csbi)) = get_csbi() { + unsafe { + SetConsoleCursorPosition(hand, COORD { + X: 0 as i16, + Y: csbi.dwCursorPosition.Y as i16 - n, + }); + } + } + "".to_string() +} + +fn get_csbi() -> Option<(self::winapi::HANDLE, self::winapi::CONSOLE_SCREEN_BUFFER_INFO)> { use self::winapi::HANDLE; use self::kernel32::{GetStdHandle, GetConsoleScreenBufferInfo}; use self::winapi::STD_OUTPUT_HANDLE; @@ -28,12 +52,8 @@ pub fn terminal_size() -> Option<(Width, Height)> { }, dwMaximumWindowSize: zc, }; - let success: bool = unsafe { GetConsoleScreenBufferInfo(hand, &mut csbi) != 0 }; - if success { - let w: Width = Width((csbi.srWindow.Right - csbi.srWindow.Left) as u16); - let h: Height = Height((csbi.srWindow.Bottom - csbi.srWindow.Top) as u16); - Some((w, h)) - } else { - None + match unsafe { GetConsoleScreenBufferInfo(hand, &mut csbi) } { + 0 => None, + _ => Some((hand, csbi)), } } From 303f46105bd9a6f7b7ca087604f8c725315f02f5 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Wed, 19 Oct 2016 21:45:10 +0300 Subject: [PATCH 8/8] fix method access follow nabijaczleweli comment --- src/tty/windows.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tty/windows.rs b/src/tty/windows.rs index ac11137e..c7fe29ca 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -17,14 +17,16 @@ pub fn terminal_size() -> Option<(Width, Height)> { } } -fn move_cursor_up(n: usize) -> String { +/// move the cursor `n` lines up; return an empty string, just to +/// be aligned with the unix version. +pub fn move_cursor_up(n: usize) -> String { use self::kernel32::SetConsoleCursorPosition; use self::winapi::COORD; if let Some((hand, csbi)) = get_csbi() { unsafe { SetConsoleCursorPosition(hand, COORD { - X: 0 as i16, - Y: csbi.dwCursorPosition.Y as i16 - n, + X: 0, + Y: csbi.dwCursorPosition.Y - n as i16, }); } }