Add ProgressReader to the utils module

Should allow to monitor a file download in a progress bar
This commit is contained in:
Alexandre Bury 2016-07-29 23:48:19 -07:00
parent 24e4e41a85
commit 6e247efc1c
3 changed files with 100 additions and 55 deletions

View File

@ -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<Item = &'a str>>(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
}
}

62
src/utils/mod.rs Normal file
View File

@ -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<Item = &'a str>>(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
}
}

36
src/utils/reader.rs Normal file
View File

@ -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<R: Read> {
reader: R,
counter: Counter,
}
impl<R: Read> ProgressReader<R> {
/// 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<R: Read> Read for ProgressReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let result = try!(self.reader.read(buf));
self.counter.tick(result);
Ok(result)
}
}