From 40bb1f3f0a8823caa020d3f7350b44ad16c2c601 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Sat, 19 Dec 2020 11:57:21 -0500 Subject: [PATCH 1/2] Write output to stderr instead of stdout This is more appropriate for dynamic output that's not intended to be machine-readable. `std::io::Stderr` is unbuffered by default, so add explicit line-buffering to prevent the terminal cursor from jumping around. Also update the examples to write their messages to stderr as well, for consistency. --- examples/cancel.rs | 4 ++-- examples/curl.rs | 4 ++-- examples/multi.rs | 4 ++-- examples/single.rs | 4 ++-- src/lib.rs | 37 +++++++++++++++++++++++-------------- 5 files changed, 31 insertions(+), 22 deletions(-) diff --git a/examples/cancel.rs b/examples/cancel.rs index dbbc108..787a9e6 100644 --- a/examples/cancel.rs +++ b/examples/cancel.rs @@ -9,7 +9,7 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; fn main() { - println!("Starting bars..."); + eprintln!("Starting bars..."); let progress = Arc::new(Mutex::new(Progress::new())); @@ -32,5 +32,5 @@ fn main() { } }); - println!("Complete!"); + eprintln!("Complete!"); } diff --git a/examples/curl.rs b/examples/curl.rs index 2ef84d7..21cbc79 100644 --- a/examples/curl.rs +++ b/examples/curl.rs @@ -2,7 +2,7 @@ use curl::easy::Easy; use linya::{Bar, Progress}; fn main() -> Result<(), curl::Error> { - println!("Starting tarball download..."); + eprintln!("Starting tarball download..."); let url = ""; let mut progress = Progress::new(); @@ -28,6 +28,6 @@ fn main() -> Result<(), curl::Error> { // `write_function`. handle.perform()?; - println!("Complete!"); + eprintln!("Complete!"); Ok(()) } diff --git a/examples/multi.rs b/examples/multi.rs index ad25ffa..194b475 100644 --- a/examples/multi.rs +++ b/examples/multi.rs @@ -7,7 +7,7 @@ use std::time::Duration; const BAR_MAX: usize = 1234; fn main() { - println!("Starting bars..."); + eprintln!("Starting bars..."); // `Progress` on its own can't be passed between threads, so we wrap it in // the usual sharing types. @@ -37,5 +37,5 @@ fn main() { } }); - println!("Complete!"); + eprintln!("Complete!"); } diff --git a/examples/single.rs b/examples/single.rs index 991fb16..8715288 100644 --- a/examples/single.rs +++ b/examples/single.rs @@ -2,7 +2,7 @@ use linya::{Bar, Progress}; use std::time::Duration; fn main() { - println!("Starting bar..."); + eprintln!("Starting bar..."); // `Progress` is not a bar, but a "bar coordinator". let mut progress = Progress::new(); @@ -18,5 +18,5 @@ fn main() { std::thread::sleep(Duration::from_millis(60)); } - println!("Complete!"); + eprintln!("Complete!"); } diff --git a/src/lib.rs b/src/lib.rs index 805f886..42ba265 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,7 @@ #![warn(missing_docs)] #![doc(html_root_url = "https://docs.rs/linya/0.1.0")] -use std::io::{Stdout, Write}; +use std::io::{LineWriter, Stderr, Write}; use terminal_size::{terminal_size, Height, Width}; /// A progress bar "coordinator" to share between threads. @@ -103,8 +103,10 @@ use terminal_size::{terminal_size, Height, Width}; pub struct Progress { /// The drawable bars themselves. bars: Vec, - /// A shared handle to `Stdout`, for buffer flushing. - out: Stdout, + /// A shared handle to `Stderr`. + /// + /// Line-buffered so that the cursor doesn't jump around unpleasantly. + out: LineWriter, /// Terminal width and height. size: Option<(usize, usize)>, } @@ -112,7 +114,7 @@ pub struct Progress { impl Progress { /// Initialize a new progress bar coordinator. pub fn new() -> Progress { - let out = std::io::stdout(); + let out = LineWriter::new(std::io::stderr()); let bars = vec![]; let size = terminal_size().map(|(Width(w), Height(h))| (w as usize, h as usize)); Progress { bars, out, size } @@ -120,7 +122,7 @@ impl Progress { /// Like [`Progress::new`] but accepts a size hint to avoid reallocation as bar count grows. pub fn with_capacity(capacity: usize) -> Progress { - let out = std::io::stdout(); + let out = LineWriter::new(std::io::stderr()); let bars = Vec::with_capacity(capacity); let size = terminal_size().map(|(Width(w), Height(h))| (w as usize, h as usize)); Progress { bars, out, size } @@ -139,14 +141,15 @@ impl Progress { let label: String = label.into(); // An initial "empty" rendering of the new bar. - println!( + writeln!( + &mut self.out, "{:f$}] 0%", label, "", l = twidth - w - 8 - 5, f = w - ); - self.out.flush().unwrap(); + ) + .unwrap(); let bar = SubBar { curr: 0, @@ -187,7 +190,8 @@ impl Progress { let diff = 100 * (b.curr - b.prev) / b.total; if b.cancelled { - print!( + write!( + &mut self.out, "\x1B[s\x1B[{}A\r{:f$}] ???%\x1B[u\r", pos, b.label, @@ -196,12 +200,14 @@ impl Progress { "", l = term_width - w - 8 - 5, f = w, - ); + ) + .unwrap(); // Very important, or the output won't appear fluid. self.out.flush().unwrap(); } else if b.curr >= b.total { - print!( + write!( + &mut self.out, "\x1B[s\x1B[{}A\r{:f$}] 100%\x1B[u\r", pos, b.label, @@ -210,14 +216,16 @@ impl Progress { "", l = term_width - w - 8 - 5, f = w, - ); + ) + .unwrap(); self.out.flush().unwrap(); } else if diff >= 1 { b.prev = b.curr; let f = (w * b.curr / b.total).min(w - 1); let e = (w - 1) - f; - print!( + write!( + &mut self.out, "\x1B[s\x1B[{}A\r{:f$}{}{:->e$}] {:3}%\x1B[u\r", pos, b.label, @@ -230,7 +238,8 @@ impl Progress { l = term_width - w - 8 - 5, f = f, e = e - ); + ) + .unwrap(); self.out.flush().unwrap(); } } From 40d9a4735d3a6794e3b969bd1f64437daa885520 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Sat, 19 Dec 2020 13:44:20 -0500 Subject: [PATCH 2/2] Changelog entry --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d5dabd..24da9dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +#### Changed + +- Progress bars are now written to standard error instead of standard output. + See https://github.com/fosskers/linya/pull/1. + #### Fixed - Some documentation inaccuracies.