From c7c0fad57e9eb6c3cc65ada70d2039a13291e8d1 Mon Sep 17 00:00:00 2001 From: Stephen Oliver Date: Sun, 19 Feb 2017 19:13:05 -0500 Subject: [PATCH 1/2] move repeat! macro to crate root --- src/lib.rs | 7 +++++++ src/pb.rs | 6 ------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 236f7035..05ef58ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,6 +111,13 @@ macro_rules! printfl { }} } +macro_rules! repeat { + ($s: expr, $n: expr) => {{ + &repeat($s).take($n).collect::() + }} +} + + #[macro_use] extern crate time; mod tty; diff --git a/src/pb.rs b/src/pb.rs index 959c84af..3d5797a9 100644 --- a/src/pb.rs +++ b/src/pb.rs @@ -18,12 +18,6 @@ macro_rules! kb_fmt { }} } -macro_rules! repeat { - ($s: expr, $n: expr) => {{ - &repeat($s).take($n).collect::() - }} -} - const FORMAT: &'static str = "[=>-]"; const TICK_FORMAT: &'static str = "\\|/-"; const NANOS_PER_SEC: u32 = 1_000_000_000; From 2e4d00904450dc3faabfee85aefd55907d94363d Mon Sep 17 00:00:00 2001 From: Stephen Oliver Date: Sun, 19 Feb 2017 01:24:20 -0500 Subject: [PATCH 2/2] add logging support for ProgressBar and MultiBar --- src/lib.rs | 1 + src/multi.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++++++--- src/pb.rs | 47 +++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 05ef58ea..d1087761 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,6 +117,7 @@ macro_rules! repeat { }} } +const PBR_LOG_BOUNDARY: &'static str = "--PBR-LOG-BOUNDARY"; #[macro_use] extern crate time; diff --git a/src/multi.rs b/src/multi.rs index 86707ea7..b1402820 100644 --- a/src/multi.rs +++ b/src/multi.rs @@ -1,9 +1,12 @@ use pb::ProgressBar; use std::str::from_utf8; -use tty::move_cursor_up; +use tty::{Width, terminal_size, move_cursor_up}; use std::io::{Stdout, Result, Write}; use std::sync::mpsc; use std::sync::mpsc::{Sender, Receiver}; +use std::iter::repeat; + +use ::PBR_LOG_BOUNDARY; pub struct MultiBar { nlines: usize, @@ -15,6 +18,8 @@ pub struct MultiBar { chan: (Sender, Receiver), handle: T, + + width: Option, } impl MultiBar { @@ -82,6 +87,7 @@ impl MultiBar { lines: Vec::new(), chan: mpsc::channel(), handle: handle, + width: None, } } @@ -112,7 +118,16 @@ impl MultiBar { /// mb.listen(); /// ``` pub fn println(&mut self, s: &str) { - self.lines.push(s.to_owned()); + let mut out = format!("{}", s); + + let width = self.width(); + + if out.len() < width { + let gap = width - out.len(); + out = out + repeat!(" ", gap); + } + + self.lines.push(out); self.nlines += 1; } @@ -154,6 +169,7 @@ impl MultiBar { chan: self.chan.0.clone(), }, total); + p.set_width(self.width); p.is_multibar = true; p.add(0); p @@ -207,12 +223,42 @@ impl MultiBar { } else { first = false; } + + // draw the log line if we have one & scroll the log message upward to prevent it from + // being overwritten by the progress bar(s) and message strings + if let Some(log_line) = msg.log_line { + out.push_str(&format!("\r{}\n", log_line)); + } + for l in self.lines.iter() { out.push_str(&format!("\r{}\n", l)); } printfl!(self.handle, "{}", out); } } + + /// Set width, or `None` for default. + /// + /// # Examples + /// + /// ```ignore + /// let mut mb = MultiBar::new(...); + /// mb.set_width(Some(80)); + /// ``` + pub fn set_width(&mut self, w: Option) { + self.width = w; + } + + /// Get terminal width, from configuration, terminal size, or default(80) + fn width(&mut self) -> usize { + if let Some(w) = self.width { + w + } else if let Some((Width(w), _)) = terminal_size() { + w as usize + } else { + 80 + } + } } pub struct Pipe { @@ -223,12 +269,30 @@ pub struct Pipe { impl Write for Pipe { fn write(&mut self, buf: &[u8]) -> Result { let s = from_utf8(buf).unwrap().to_owned(); + + // check to see if ProgressBar set a logging boundary & split out the log if we find it + let (log_line, bar) = match s.contains(PBR_LOG_BOUNDARY) { + true => { + let v: Vec<&str> = s.split(PBR_LOG_BOUNDARY).collect(); + let log_line = Some(v[0].to_owned()); + + let bar = v[1].to_owned(); + + (log_line, bar) + }, + false => { + (None, s) + } + + }; + self.chan .send(WriteMsg { // finish method emit empty string - done: s == "", + done: bar == "", level: self.level, - string: s, + string: bar, + log_line: log_line, }) .unwrap(); Ok(1) @@ -245,4 +309,5 @@ struct WriteMsg { done: bool, level: usize, string: String, + log_line: Option, } diff --git a/src/pb.rs b/src/pb.rs index 3d5797a9..022936e5 100644 --- a/src/pb.rs +++ b/src/pb.rs @@ -5,6 +5,8 @@ use time::{self, SteadyTime}; use std::io::Stdout; use tty::{Width, terminal_size}; +use ::PBR_LOG_BOUNDARY; + macro_rules! kb_fmt { ($n: ident) => {{ let kb = 1024f64; @@ -44,6 +46,7 @@ pub struct ProgressBar { tick_state: usize, width: Option, message: String, + log_line: Option, last_refresh_time: SteadyTime, max_refresh_rate: Option, pub is_finish: bool, @@ -126,6 +129,7 @@ impl ProgressBar { tick_state: 0, width: None, message: String::new(), + log_line: None, last_refresh_time: SteadyTime::now(), max_refresh_rate: None, handle: handle, @@ -384,6 +388,32 @@ impl ProgressBar { let gap = width - out.len(); out = out + repeat!(" ", gap); } + + // handle a log line waiting to be printed + if let Some(ref log_line) = self.log_line { + + // overwrite the current line with our log message + whitespace + let mut log_out = format!("\r{}", log_line); + + if log_line.len() < width { + log_out += repeat!(" ", width - log_line.len()); + }; + + // if writing to a MultiBar, use a boundary string to allow MultiBar to print the log + // and bar separately. + // + // otherwise print a newline to scroll the log message upward to prevent it from being + // overwritten by the progress bar + if self.is_multibar { + log_out = log_out + PBR_LOG_BOUNDARY; + } else { + log_out = log_out + "\n"; + } + + out = log_out + &out; + } + self.log_line = None; + // print printfl!(self.handle, "\r{}", out); @@ -407,6 +437,10 @@ impl ProgressBar { redraw = true; } + if let Some(_) = self.log_line { + redraw = true; + } + if redraw { self.draw(); } @@ -434,6 +468,19 @@ impl ProgressBar { } + /// Write string `s` above the progress bar for logging + /// + /// Log messages will appear to scroll upward while the progress bar(s) stay in place. + /// + /// Behavior should be the same whether the bar is part of a MultiBar or not + /// + pub fn log(&mut self, s: &str) { + self.log_line = Some(s.to_owned()); + self.draw(); + } + + + /// Call finish and write string `s` below the progress bar. /// /// If the ProgressBar is part of MultiBar instance, you should use