1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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);
    }
}