use crate::bar::progress::ProgressBar;
use crate::style::style::ProgressStyle;
#[derive(Clone, Debug)]
pub struct ProgressBarIter<I> {
iter: I,
pub(crate) bar: ProgressBar,
}
impl<I: Iterator> ProgressBarIter<I> {
pub(crate) fn new(iter: I, bar: ProgressBar) -> Self {
Self { iter, bar }
}
}
impl<I: Iterator> Iterator for ProgressBarIter<I> {
type Item = I::Item;
fn next(&mut self) -> Option<I::Item> {
let item = self.iter.next();
match &item {
Some(_) => self.bar.inc(1),
None => {
if !self.bar.is_finished() {
self.bar.finish();
}
}
}
item
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<I: ExactSizeIterator> ExactSizeIterator for ProgressBarIter<I> {
fn len(&self) -> usize {
self.iter.len()
}
}
impl<I: DoubleEndedIterator> DoubleEndedIterator for ProgressBarIter<I> {
fn next_back(&mut self) -> Option<Self::Item> {
let item = self.iter.next_back();
match &item {
Some(_) => self.bar.inc(1),
None => {
if !self.bar.is_finished() {
self.bar.finish();
}
}
}
item
}
}
pub trait ProgressIterator: Iterator + Sized {
fn progress(self) -> ProgressBarIter<Self>;
fn progress_with(self, pb: ProgressBar) -> ProgressBarIter<Self>;
fn progress_with_style(self, style: ProgressStyle) -> ProgressBarIter<Self>;
fn progress_count(self, len: u64) -> ProgressBarIter<Self>;
}
impl<I: Iterator> ProgressIterator for I {
fn progress(self) -> ProgressBarIter<Self> {
let lower = self.size_hint().0;
if lower > 0 {
self.progress_count(lower as u64)
} else {
ProgressBarIter::new(self, ProgressBar::new_spinner())
}
}
fn progress_with(self, pb: ProgressBar) -> ProgressBarIter<Self> {
ProgressBarIter::new(self, pb)
}
fn progress_with_style(self, style: ProgressStyle) -> ProgressBarIter<Self> {
let lower = self.size_hint().0;
let pb = if lower > 0 {
ProgressBar::builder()
.length(lower as u64)
.style(style)
.build()
} else {
ProgressBar::builder().style(style).build()
};
ProgressBarIter::new(self, pb)
}
fn progress_count(self, len: u64) -> ProgressBarIter<Self> {
ProgressBarIter::new(self, ProgressBar::new(len))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_progress_iter_advances_bar() {
let pb = ProgressBar::hidden();
let mut iter = (0..3).progress_with(pb.clone());
assert_eq!(iter.next(), Some(0));
assert_eq!(pb.position(), 1);
}
#[test]
fn test_progress_iter_finishes_at_end() {
let pb = ProgressBar::hidden();
let mut iter = (0..1).progress_with(pb.clone());
assert_eq!(iter.next(), Some(0));
assert_eq!(iter.next(), None);
assert!(pb.is_finished());
}
#[test]
fn test_progress_iter_count_sets_length() {
let iter = (0..3).progress_count(3);
assert_eq!(iter.bar.length(), Some(3));
}
#[test]
fn test_progress_iter_size_hint_used() {
let iter = (0..5).progress();
assert_eq!(iter.bar.length(), Some(5));
}
#[test]
fn test_progress_iter_with_custom_bar() {
let pb = ProgressBar::hidden();
let iter = (0..5).progress_with(pb.clone());
assert!(iter.bar.same_bar(&pb));
}
#[test]
fn test_progress_iter_collects_all_items() {
let pb = ProgressBar::hidden();
let values: Vec<_> = (0..4).progress_with(pb).collect();
assert_eq!(values, vec![0, 1, 2, 3]);
}
#[test]
fn test_progress_iter_on_empty() {
let pb = ProgressBar::hidden();
let values: Vec<i32> = std::iter::empty().progress_with(pb.clone()).collect();
assert!(values.is_empty());
assert!(pb.is_finished());
}
#[test]
fn test_progress_iter_double_ended() {
let pb = ProgressBar::hidden();
let mut iter = (0..3).progress_with(pb.clone());
assert_eq!(iter.next_back(), Some(2));
assert_eq!(pb.position(), 1);
}
}