diff --git a/Cargo.toml b/Cargo.toml index 69bc1be..e098758 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,7 @@ version = "1.5.0" [dev-dependencies] rand = "0.4" +pretty-bytes = "0.2.2" [features] blt-backend = ["bear-lib-terminal"] diff --git a/examples/vpv.rs b/examples/vpv.rs new file mode 100644 index 0000000..c3ba833 --- /dev/null +++ b/examples/vpv.rs @@ -0,0 +1,139 @@ +extern crate cursive; +extern crate pretty_bytes; + +use std::io; + +use cursive::Cursive; +use cursive::traits::{Boxable, With}; +use cursive::utils; +use cursive::views::{Canvas, Dialog, LinearLayout, ProgressBar}; +use pretty_bytes::converter::convert; +use std::thread; +use std::time; + +// This example is a visual version of the `pv` tool. + +fn main() { + let mut siv = Cursive::default(); + + // We'll use this channel to signal the end of the transfer + let cb_sink = siv.cb_sink().clone(); + + // Use a counter to track progress + let counter = utils::Counter::new(0); + let counter_copy = counter.clone(); + let start = time::Instant::now(); + + // If an argument is given, it is the file we'll read from. + // Else, read from stdin. + let (source, len) = match std::env::args().nth(1) { + Some(source) => { + let meta = std::fs::metadata(&source).unwrap(); + // If possible, read the file size to have a progress bar. + let len = meta.len(); + ( + Some(source), + if len > 0 { Some(len) } else { None }, + ) + } + None => (None, None), + }; + + // Start the copy in a separate thread + thread::spawn(move || { + // Copy to stdout - lock it for better performance. + let stdout = io::stdout(); + let mut stdout = stdout.lock(); + + match source { + None => { + // Copy from stdin - lock it for better performance. + let stdin = io::stdin(); + let stdin = stdin.lock(); + let mut reader = + utils::ProgressReader::new(counter_copy, stdin); + + // And copy! + io::copy(&mut reader, &mut stdout).unwrap(); + } + Some(source) => { + // Copy from stdin - lock it for better performance. + let input = std::fs::File::open(source).unwrap(); + let mut reader = + utils::ProgressReader::new(counter_copy, input); + + // And copy! + io::copy(&mut reader, &mut stdout).unwrap(); + } + } + + // When we're done, shut down the application + cb_sink + .send(Box::new(|s: &mut Cursive| s.quit())) + .unwrap(); + }); + + // Add a single view: progress status + siv.add_layer( + Dialog::new().title("Copying...").content( + LinearLayout::vertical() + .child( + Canvas::new(counter.clone()) + .with_draw(move |c, printer| { + let ticks = c.get() as f64; + let now = time::Instant::now(); + let duration = now - start; + + let seconds = duration.as_secs() as f64 + + duration.subsec_nanos() as f64 * 1e-9; + + let speed = ticks / seconds; + + // Print ETA if we have a file size + // Otherwise prints elapsed time. + if let Some(len) = len { + let remaining = (len as f64 - ticks) / speed; + printer.print( + (0, 0), + &format!( + "ETA: {:.1} seconds", + remaining + ), + ); + } else { + printer.print( + (0, 0), + &format!( + "Elapsed: {:.1} seconds", + seconds + ), + ); + } + printer.print( + (0, 1), + &format!("Copied: {}", convert(ticks)), + ); + printer.print( + (0, 2), + &format!("Speed: {}/s", convert(speed)), + ); + }) + .fixed_size((25, 3)), + ) + .with(|l| { + // If we have a file length, add a progress bar + if let Some(len) = len { + l.add_child( + ProgressBar::new() + .max(len as usize) + .with_value(counter.clone()), + ); + } + }), + ), + ); + + siv.set_fps(10); + + siv.run(); +} diff --git a/src/utils/reader.rs b/src/utils/reader.rs index 03ccad0..1f5485f 100644 --- a/src/utils/reader.rs +++ b/src/utils/reader.rs @@ -32,7 +32,7 @@ impl ProgressReader { impl Read for ProgressReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { - let result = try!(self.reader.read(buf)); + let result = self.reader.read(buf)?; self.counter.tick(result); Ok(result) } diff --git a/src/with.rs b/src/with.rs index ba6df23..b183737 100644 --- a/src/with.rs +++ b/src/with.rs @@ -14,6 +14,17 @@ pub trait With: Sized { f(&mut self)?; Ok(self) } + + /// Calls the given closure if `condition == true`. + fn with_if(mut self, condition: bool, f: F) -> Self + where + F: FnOnce(&mut Self), + { + if condition { + f(&mut self); + } + self + } } impl With for T {}