rs-progress 0.1.1

Command line Progress made easy
Documentation
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; // # character
            }

            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);
    }
}