1use std::borrow::Cow;
2use std::io::{self, IoSliceMut};
3use std::iter::FusedIterator;
4#[cfg(feature = "tokio")]
5use std::pin::Pin;
6#[cfg(feature = "tokio")]
7use std::task::{Context, Poll};
8use std::time::Duration;
9
10#[cfg(feature = "tokio")]
11use tokio::io::{ReadBuf, SeekFrom};
12
13use crate::progress_bar::ProgressBar;
14use crate::state::ProgressFinish;
15use crate::style::ProgressStyle;
16
17pub trait ProgressIterator
19where
20 Self: Sized + Iterator,
21{
22 fn try_progress(self) -> Option<ProgressBarIter<Self>> {
27 self.size_hint()
28 .1
29 .map(|len| self.progress_count(u64::try_from(len).unwrap()))
30 }
31
32 fn progress(self) -> ProgressBarIter<Self>
34 where
35 Self: ExactSizeIterator,
36 {
37 let len = u64::try_from(self.len()).unwrap();
38 self.progress_count(len)
39 }
40
41 fn progress_count(self, len: u64) -> ProgressBarIter<Self> {
43 self.progress_with(ProgressBar::new(len))
44 }
45
46 fn progress_with(self, progress: ProgressBar) -> ProgressBarIter<Self>;
48
49 fn progress_with_style(self, style: crate::ProgressStyle) -> ProgressBarIter<Self>
51 where
52 Self: ExactSizeIterator,
53 {
54 let len = u64::try_from(self.len()).unwrap();
55 let bar = ProgressBar::new(len).with_style(style);
56 self.progress_with(bar)
57 }
58}
59
60#[derive(Debug)]
62pub struct ProgressBarIter<T> {
63 pub(crate) it: T,
64 pub progress: ProgressBar,
65 pub(crate) seek_max: SeekMax,
66}
67
68impl<T> ProgressBarIter<T> {
69 pub fn with_style(mut self, style: ProgressStyle) -> Self {
73 self.progress = self.progress.with_style(style);
74 self
75 }
76
77 pub fn with_prefix(mut self, prefix: impl Into<Cow<'static, str>>) -> Self {
81 self.progress = self.progress.with_prefix(prefix);
82 self
83 }
84
85 pub fn with_message(mut self, message: impl Into<Cow<'static, str>>) -> Self {
89 self.progress = self.progress.with_message(message);
90 self
91 }
92
93 pub fn with_position(mut self, position: u64) -> Self {
97 self.progress = self.progress.with_position(position);
98 self
99 }
100
101 pub fn with_elapsed(mut self, elapsed: Duration) -> Self {
105 self.progress = self.progress.with_elapsed(elapsed);
106 self
107 }
108
109 pub fn with_finish(mut self, finish: ProgressFinish) -> Self {
113 self.progress = self.progress.with_finish(finish);
114 self
115 }
116}
117
118impl<S, T: Iterator<Item = S>> Iterator for ProgressBarIter<T> {
119 type Item = S;
120
121 fn next(&mut self) -> Option<Self::Item> {
122 let item = self.it.next();
123
124 if item.is_some() {
125 self.progress.inc(1);
126 } else if !self.progress.is_finished() {
127 self.progress.finish_using_style();
128 }
129
130 item
131 }
132}
133
134impl<T: ExactSizeIterator> ExactSizeIterator for ProgressBarIter<T> {
135 fn len(&self) -> usize {
136 self.it.len()
137 }
138}
139
140impl<T: DoubleEndedIterator> DoubleEndedIterator for ProgressBarIter<T> {
141 fn next_back(&mut self) -> Option<Self::Item> {
142 let item = self.it.next_back();
143
144 if item.is_some() {
145 self.progress.inc(1);
146 } else if !self.progress.is_finished() {
147 self.progress.finish_using_style();
148 }
149
150 item
151 }
152}
153
154impl<T: FusedIterator> FusedIterator for ProgressBarIter<T> {}
155
156impl<R: io::Read> io::Read for ProgressBarIter<R> {
157 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
158 let inc = self.it.read(buf)?;
159 self.progress.set_position(
160 self.seek_max
161 .update_seq(self.progress.position(), inc as u64),
162 );
163 Ok(inc)
164 }
165
166 fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
167 let inc = self.it.read_vectored(bufs)?;
168 self.progress.set_position(
169 self.seek_max
170 .update_seq(self.progress.position(), inc as u64),
171 );
172 Ok(inc)
173 }
174
175 fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
176 let inc = self.it.read_to_string(buf)?;
177 self.progress.set_position(
178 self.seek_max
179 .update_seq(self.progress.position(), inc as u64),
180 );
181 Ok(inc)
182 }
183
184 fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
185 self.it.read_exact(buf)?;
186 self.progress.set_position(
187 self.seek_max
188 .update_seq(self.progress.position(), buf.len() as u64),
189 );
190 Ok(())
191 }
192}
193
194impl<R: io::BufRead> io::BufRead for ProgressBarIter<R> {
195 fn fill_buf(&mut self) -> io::Result<&[u8]> {
196 self.it.fill_buf()
197 }
198
199 fn consume(&mut self, amt: usize) {
200 self.it.consume(amt);
201 self.progress.set_position(
202 self.seek_max
203 .update_seq(self.progress.position(), amt.try_into().unwrap()),
204 );
205 }
206}
207
208impl<S: io::Seek> io::Seek for ProgressBarIter<S> {
209 fn seek(&mut self, f: io::SeekFrom) -> io::Result<u64> {
210 self.it.seek(f).inspect(|&pos| {
211 if f != io::SeekFrom::Current(0) {
212 self.progress.set_position(self.seek_max.update_seek(pos));
215 }
216 })
217 }
218 fn stream_position(&mut self) -> io::Result<u64> {
221 self.it.stream_position()
222 }
223}
224
225#[derive(Debug, Default)]
230pub(crate) struct SeekMax<const RESET: u8 = 5, const HISTORY: usize = 10> {
231 buf: Option<(Box<MaxRingBuf<HISTORY>>, u8)>,
232}
233
234impl<const RESET: u8, const HISTORY: usize> SeekMax<RESET, HISTORY> {
235 fn update_seq(&mut self, prev_pos: u64, delta: u64) -> u64 {
236 let new_pos = prev_pos + delta;
237 let Some((buf, seq)) = &mut self.buf else {
238 return new_pos;
239 };
240
241 *seq += 1;
242 if *seq >= RESET {
243 self.buf = None;
244 return new_pos;
245 }
246
247 buf.update(new_pos);
248 buf.max()
249 }
250
251 fn update_seek(&mut self, newpos: u64) -> u64 {
252 let (b, seq) = self
253 .buf
254 .get_or_insert_with(|| (Box::new(MaxRingBuf::<HISTORY>::default()), 0));
255 *seq = 0;
256 b.update(newpos);
257 b.max()
258 }
259}
260
261#[derive(Debug)]
263struct MaxRingBuf<const HISTORY: usize = 10> {
264 history: [u64; HISTORY],
265 head: u8, max_pos: u8, }
268
269impl<const HISTORY: usize> MaxRingBuf<HISTORY> {
270 fn update(&mut self, new: u64) {
281 let head = usize::from(self.head) % self.history.len();
282 let max_pos = usize::from(self.max_pos) % self.history.len();
283 let prev_max = self.history[max_pos];
284 self.history[head] = new;
285
286 if new > prev_max {
287 self.max_pos = self.head;
289 } else if self.max_pos == self.head && new < prev_max {
290 let (idx, _val) = self
293 .history
294 .iter()
295 .enumerate()
296 .max_by_key(|(_, v)| *v)
297 .expect("array has fixded size > 0");
298 self.max_pos = idx as u8;
300 }
301
302 self.head = (self.head + 1) % (self.history.len() as u8);
303 }
304
305 fn max(&self) -> u64 {
306 self.history[self.max_pos as usize % self.history.len()]
308 }
309}
310
311impl<const HISTORY: usize> Default for MaxRingBuf<HISTORY> {
312 fn default() -> Self {
313 assert!(HISTORY <= u8::MAX.into());
314 assert!(HISTORY > 0);
315 Self {
316 history: [0; HISTORY],
317 head: 0,
318 max_pos: 0,
319 }
320 }
321}
322
323#[cfg(feature = "tokio")]
324#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
325impl<W: tokio::io::AsyncWrite + Unpin> tokio::io::AsyncWrite for ProgressBarIter<W> {
326 fn poll_write(
327 mut self: Pin<&mut Self>,
328 cx: &mut Context<'_>,
329 buf: &[u8],
330 ) -> Poll<io::Result<usize>> {
331 Pin::new(&mut self.it).poll_write(cx, buf).map(|poll| {
332 poll.inspect(|&inc| {
333 let pos = self.progress.position();
334 let new = self.seek_max.update_seq(pos, inc as u64);
335 self.progress.set_position(new);
336 })
337 })
338 }
339
340 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
341 Pin::new(&mut self.it).poll_flush(cx)
342 }
343
344 fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
345 Pin::new(&mut self.it).poll_shutdown(cx)
346 }
347}
348
349#[cfg(feature = "tokio")]
350#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
351impl<W: tokio::io::AsyncRead + Unpin> tokio::io::AsyncRead for ProgressBarIter<W> {
352 fn poll_read(
353 mut self: Pin<&mut Self>,
354 cx: &mut Context<'_>,
355 buf: &mut ReadBuf<'_>,
356 ) -> Poll<io::Result<()>> {
357 let prev_len = buf.filled().len() as u64;
358 let poll = Pin::new(&mut self.it).poll_read(cx, buf);
359 if let Poll::Ready(_e) = &poll {
360 let inc = buf.filled().len() as u64 - prev_len;
361 let pos = self.progress.position();
362 let new = self.seek_max.update_seq(pos, inc);
363 self.progress.set_position(new);
364 }
365 poll
366 }
367}
368
369#[cfg(feature = "tokio")]
370#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
371impl<W: tokio::io::AsyncSeek + Unpin> tokio::io::AsyncSeek for ProgressBarIter<W> {
372 fn start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()> {
373 Pin::new(&mut self.it).start_seek(position)
374 }
375
376 fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
377 let poll = Pin::new(&mut self.it).poll_complete(cx);
378 if let Poll::Ready(Ok(pos)) = &poll {
379 let new = self.seek_max.update_seek(*pos);
380 self.progress.set_position(new);
381 }
382
383 poll
384 }
385}
386
387#[cfg(feature = "tokio")]
388#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
389impl<W: tokio::io::AsyncBufRead + Unpin + tokio::io::AsyncRead> tokio::io::AsyncBufRead
390 for ProgressBarIter<W>
391{
392 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
393 let this = self.get_mut();
394 Pin::new(&mut this.it).poll_fill_buf(cx)
395 }
396
397 fn consume(mut self: Pin<&mut Self>, amt: usize) {
398 Pin::new(&mut self.it).consume(amt);
399 let pos = self.progress.position();
400 let new = self.seek_max.update_seq(pos, amt as u64);
401 self.progress.set_position(new);
402 }
403}
404
405#[cfg(feature = "futures")]
406#[cfg_attr(docsrs, doc(cfg(feature = "futures")))]
407impl<S: futures_core::Stream + Unpin> futures_core::Stream for ProgressBarIter<S> {
408 type Item = S::Item;
409
410 fn poll_next(
411 self: std::pin::Pin<&mut Self>,
412 cx: &mut std::task::Context<'_>,
413 ) -> std::task::Poll<Option<Self::Item>> {
414 let this = self.get_mut();
415 let item = std::pin::Pin::new(&mut this.it).poll_next(cx);
416 match &item {
417 std::task::Poll::Ready(Some(_)) => this.progress.inc(1),
418 std::task::Poll::Ready(None) => this.progress.finish_using_style(),
419 std::task::Poll::Pending => {}
420 }
421 item
422 }
423}
424
425impl<W: io::Write> io::Write for ProgressBarIter<W> {
426 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
427 self.it.write(buf).inspect(|&inc| {
428 self.progress.set_position(
429 self.seek_max
430 .update_seq(self.progress.position(), inc as u64),
431 );
432 })
433 }
434
435 fn write_vectored(&mut self, bufs: &[io::IoSlice]) -> io::Result<usize> {
436 self.it.write_vectored(bufs).inspect(|&inc| {
437 self.progress.set_position(
438 self.seek_max
439 .update_seq(self.progress.position(), inc as u64),
440 );
441 })
442 }
443
444 fn flush(&mut self) -> io::Result<()> {
445 self.it.flush()
446 }
447
448 }
452
453impl<S, T: Iterator<Item = S>> ProgressIterator for T {
454 fn progress_with(self, progress: ProgressBar) -> ProgressBarIter<Self> {
455 ProgressBarIter {
456 it: self,
457 progress,
458 seek_max: SeekMax::default(),
459 }
460 }
461}
462
463#[cfg(test)]
464mod test {
465 use crate::iter::{ProgressBarIter, ProgressIterator};
466 use crate::progress_bar::ProgressBar;
467 use crate::ProgressStyle;
468
469 #[test]
470 fn it_can_wrap_an_iterator() {
471 let v = [1, 2, 3];
472 let wrap = |it: ProgressBarIter<_>| {
473 assert_eq!(it.map(|x| x * 2).collect::<Vec<_>>(), vec![2, 4, 6]);
474 };
475
476 wrap(v.iter().progress());
477 wrap(v.iter().progress_count(3));
478 wrap({
479 let pb = ProgressBar::new(v.len() as u64);
480 v.iter().progress_with(pb)
481 });
482 wrap({
483 let style = ProgressStyle::default_bar()
484 .template("{wide_bar:.red} {percent}/100%")
485 .unwrap();
486 v.iter().progress_with_style(style)
487 });
488 }
489
490 #[test]
491 fn test_max_ring_buf() {
492 use crate::iter::MaxRingBuf;
493 let mut max = MaxRingBuf::<10>::default();
494 max.update(100);
495 assert_eq!(max.max(), 100);
496 for i in 0..10 {
497 max.update(99 - i);
498 }
499 assert_eq!(max.max(), 99);
500 }
501}