rs_progress/
lib.rs

1use std::io::{Stdout, stdout, Write};
2use std::time::SystemTime;
3
4pub struct Progress<T> {
5    current_index: usize,
6    start_time: SystemTime,
7    iter_size: usize,
8    inner: Box<dyn Iterator<Item=T>>,
9    progbar: Vec<u8>,
10    progbar_size: usize,
11    output: Stdout
12}
13
14
15impl<T> Progress<T> {
16    fn size_hint(mut self, size:usize) -> Progress<T> {
17        self.iter_size = size;
18
19        let prog_size = if size > 60 { 60 } else {size};
20
21        if self.progbar_size != prog_size {
22            self.progbar_size = prog_size;
23            self.progbar = vec![0x20; prog_size];
24        }
25        self
26    }
27}
28
29
30pub fn progress<T>(boxed: Box<dyn Iterator<Item=T>>) -> Progress<T> {
31
32    let p = Progress{
33        current_index:0,
34        start_time: SystemTime::now(),
35        iter_size: 0,
36        inner: boxed,
37        progbar: vec![],
38        progbar_size: 0,
39        output: stdout(),
40    };
41    let size = p.inner.size_hint().0;
42    p.size_hint(size)
43}
44
45
46pub trait Progressable<T> : Iterator<Item=T> where Self: Sized, Self: 'static {
47    #[inline]
48    fn prog(self) -> Progress<T> {
49        let b = Box::new(self);
50        progress(b)
51    }
52}
53
54impl<T, V> Progressable<V> for T where T : Iterator<Item=V>, T: 'static {}
55
56impl<T> Iterator for Progress<T> {
57    type Item = T;
58    fn next(&mut self) -> Option<T> {
59        let n = self.inner.next();
60
61        if n.is_some() {
62            let i = self.current_index * self.progbar_size / self.iter_size;
63            let j = ((self.current_index as i128 - 1) & (1 as i128).rotate_right(1)) as usize * self.progbar_size / self.iter_size;
64
65            if i != j || i == 0 {
66                self.progbar[i] = 0x23; // # character
67            }
68
69            let iter_rate = match self.start_time.elapsed()  {
70                Ok(elapsed) => match elapsed.as_secs() {
71                    e if e > 0 => self.current_index as f32 / (e) as f32,
72                    _ => -1.0
73                },
74                Err(_) => -1.0
75            };
76
77            if iter_rate == -1.0 {
78
79            print!("\r[{bar}] {cur}/{max} [? it/sec]       ", 
80                bar=String::from_utf8(self.progbar.clone()).unwrap(),
81                cur=self.current_index,
82                max=self.iter_size
83            );
84            }
85            else {
86
87            print!("\r[{bar}] {cur}/{max} [{rate:.3} it/sec]        ", 
88                bar=String::from_utf8(self.progbar.clone()).unwrap(),
89                cur=self.current_index,
90                max=self.iter_size,
91                rate=iter_rate
92            );
93            }
94            self.output.flush().unwrap();
95
96        }
97        else if self.current_index ==  self.iter_size {
98            println!("")
99        }
100
101        self.current_index += 1;
102        n
103    }
104}
105
106
107#[cfg(test)]
108mod tests {
109
110    use super::*;
111    use std::array::IntoIter;
112
113    fn test_call() {
114        let arr = [1,2,3,4,5,6,7,8,9];
115        let sum = IntoIter::new(arr).prog().reduce(|a,b| {a+b}).unwrap();
116
117        assert_eq!(45, sum);
118
119        let range = 1..10;
120        let double_sum = range.into_iter().map(|e| {e*2}).reduce(|a,b| {a+b}).unwrap();
121
122        assert_eq!(90, double_sum);
123
124
125        let s = "qwerty";
126        let mut count = 0;
127        for c in s.chars().prog() {
128            count+=1;
129        }
130
131        assert_eq!(6, count);
132    }
133}