use std::io::{Stdout, stdout, Write};
use std::time::SystemTime;
pub struct Progress<T> {
current_index: usize,
start_time: SystemTime,
iter_size: usize,
inner: Box<dyn Iterator<Item=T>>,
progbar: Vec<u8>,
progbar_size: usize,
output: Stdout
}
impl<T> Progress<T> {
fn size_hint(mut self, size:usize) -> Progress<T> {
self.iter_size = size;
let prog_size = if size > 60 { 60 } else {size};
if self.progbar_size != prog_size {
self.progbar_size = prog_size;
self.progbar = vec![0x20; prog_size];
}
self
}
}
pub fn progress<T>(boxed: Box<dyn Iterator<Item=T>>) -> Progress<T> {
let p = Progress{
current_index:0,
start_time: SystemTime::now(),
iter_size: 0,
inner: boxed,
progbar: vec![],
progbar_size: 0,
output: stdout(),
};
let size = p.inner.size_hint().0;
p.size_hint(size)
}
pub trait Progressable<T> : Iterator<Item=T> where Self: Sized, Self: 'static {
#[inline]
fn prog(self) -> Progress<T> {
let b = Box::new(self);
progress(b)
}
}
impl<T, V> Progressable<V> for T where T : Iterator<Item=V>, T: 'static {}
impl<T> Iterator for Progress<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
let n = self.inner.next();
if n.is_some() {
let i = self.current_index * self.progbar_size / self.iter_size;
let j = ((self.current_index as i128 - 1) & (1 as i128).rotate_right(1)) as usize * self.progbar_size / self.iter_size;
if i != j || i == 0 {
self.progbar[i] = 0x23; }
let iter_rate = match self.start_time.elapsed() {
Ok(elapsed) => match elapsed.as_secs() {
e if e > 0 => self.current_index as f32 / (e) as f32,
_ => -1.0
},
Err(_) => -1.0
};
if iter_rate == -1.0 {
print!("\r[{bar}] {cur}/{max} [? it/sec] ",
bar=String::from_utf8(self.progbar.clone()).unwrap(),
cur=self.current_index,
max=self.iter_size
);
}
else {
print!("\r[{bar}] {cur}/{max} [{rate:.3} it/sec] ",
bar=String::from_utf8(self.progbar.clone()).unwrap(),
cur=self.current_index,
max=self.iter_size,
rate=iter_rate
);
}
self.output.flush().unwrap();
}
else if self.current_index == self.iter_size {
println!("")
}
self.current_index += 1;
n
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::array::IntoIter;
fn test_call() {
let arr = [1,2,3,4,5,6,7,8,9];
let sum = IntoIter::new(arr).prog().reduce(|a,b| {a+b}).unwrap();
assert_eq!(45, sum);
let range = 1..10;
let double_sum = range.into_iter().map(|e| {e*2}).reduce(|a,b| {a+b}).unwrap();
assert_eq!(90, double_sum);
let s = "qwerty";
let mut count = 0;
for c in s.chars().prog() {
count+=1;
}
assert_eq!(6, count);
}
}