From 6e247efc1ce51529ee8e880aaa250ded0595f058 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Fri, 29 Jul 2016 23:48:19 -0700 Subject: [PATCH] Add `ProgressReader` to the `utils` module Should allow to monitor a file download in a progress bar --- src/{utils.rs => utils/lines_iterator.rs} | 57 +-------------------- src/utils/mod.rs | 62 +++++++++++++++++++++++ src/utils/reader.rs | 36 +++++++++++++ 3 files changed, 100 insertions(+), 55 deletions(-) rename src/{utils.rs => utils/lines_iterator.rs} (60%) create mode 100644 src/utils/mod.rs create mode 100644 src/utils/reader.rs diff --git a/src/utils.rs b/src/utils/lines_iterator.rs similarity index 60% rename from src/utils.rs rename to src/utils/lines_iterator.rs index 91f3f27..229ef70 100644 --- a/src/utils.rs +++ b/src/utils/lines_iterator.rs @@ -1,8 +1,8 @@ -//! Toolbox to make text layout easier. - use unicode_width::UnicodeWidthStr; use unicode_segmentation::UnicodeSegmentation; +use utils::prefix_length; + /// Generates rows of text in constrained width. /// /// Given a long text and a width constraint, it iterates over @@ -91,56 +91,3 @@ impl<'a> Iterator for LinesIterator<'a> { }) } } - -/// Computes a sub-string length that fits in the given `width`. -/// -/// Takes non-breakable elements from `iter`, while keeping the -/// string width under `width` (and adding the length of `delimiter` -/// between each element). -/// -/// Given `total_text = iter.collect().join(delimiter)`, the result -/// is the length of the longest prefix of `width` or less cells, -/// without breaking inside an element. -/// -/// Example: -/// -/// ``` -/// # extern crate cursive; -/// extern crate unicode_segmentation; -/// use unicode_segmentation::UnicodeSegmentation; -/// -/// # use cursive::utils::prefix_length; -/// # fn main() { -/// let my_text = "blah..."; -/// // This returns the number of bytes for a prefix of `my_text` that -/// // fits within 5 cells. -/// prefix_length(my_text.graphemes(true), 5, ""); -/// # } -/// ``` -pub fn prefix_length<'a, I: Iterator>(iter: I, width: usize, - delimiter: &str) - -> usize { - let delimiter_width = delimiter.width(); - let delimiter_len = delimiter.len(); - - let sum = iter.scan(0, |w, token| { - *w += token.width(); - if *w > width { - None - } else { - // Add a space - *w += delimiter_width; - Some(token) - } - }) - .map(|token| token.len() + delimiter_len) - .fold(0, |a, b| a + b); - - // We counted delimiter once too many times, - // but only if the iterator was non empty. - if sum == 0 { - sum - } else { - sum - delimiter_len - } -} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..e6f516f --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,62 @@ +//! Toolbox to make text layout easier. + +use unicode_width::UnicodeWidthStr; + +mod lines_iterator; +mod reader; + +pub use self::lines_iterator::{LinesIterator, Row}; +pub use self::reader::ProgressReader; + +/// Computes a sub-string length that fits in the given `width`. +/// +/// Takes non-breakable elements from `iter`, while keeping the +/// string width under `width` (and adding the length of `delimiter` +/// between each element). +/// +/// Given `total_text = iter.collect().join(delimiter)`, the result +/// is the length of the longest prefix of `width` or less cells, +/// without breaking inside an element. +/// +/// Example: +/// +/// ``` +/// # extern crate cursive; +/// extern crate unicode_segmentation; +/// use unicode_segmentation::UnicodeSegmentation; +/// +/// # use cursive::utils::prefix_length; +/// # fn main() { +/// let my_text = "blah..."; +/// // This returns the number of bytes for a prefix of `my_text` that +/// // fits within 5 cells. +/// prefix_length(my_text.graphemes(true), 5, ""); +/// # } +/// ``` +pub fn prefix_length<'a, I: Iterator>(iter: I, width: usize, + delimiter: &str) + -> usize { + let delimiter_width = delimiter.width(); + let delimiter_len = delimiter.len(); + + let sum = iter.scan(0, |w, token| { + *w += token.width(); + if *w > width { + None + } else { + // Add a space + *w += delimiter_width; + Some(token) + } + }) + .map(|token| token.len() + delimiter_len) + .fold(0, |a, b| a + b); + + // We counted delimiter once too many times, + // but only if the iterator was non empty. + if sum == 0 { + sum + } else { + sum - delimiter_len + } +} diff --git a/src/utils/reader.rs b/src/utils/reader.rs new file mode 100644 index 0000000..798a5f5 --- /dev/null +++ b/src/utils/reader.rs @@ -0,0 +1,36 @@ +use std::io::{self, Read}; + +use views::Counter; + +/// Wrapper around a `Read` that reports the progress made. +/// +/// Used to monitor a file downloading or other slow IO task +/// in a progress bar. +pub struct ProgressReader { + reader: R, + counter: Counter, +} + + +impl ProgressReader { + /// Creates a new `ProgressReader` around `reader`. + /// + /// `counter` will be updated with the number of bytes read. + /// + /// You should make sure the progress bar knows how + /// many bytes should be received. + pub fn new(counter: Counter, reader: R) -> Self { + ProgressReader { + reader: reader, + counter: counter, + } + } +} + +impl Read for ProgressReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let result = try!(self.reader.read(buf)); + self.counter.tick(result); + Ok(result) + } +}