use rayon::iter::plumbing::{Consumer, Folder, Producer, ProducerCallback, UnindexedConsumer}; use rayon::iter::{IndexedParallelIterator, ParallelIterator}; use crate::{ProgressBar, ProgressBarIter}; /// Wraps a Rayon parallel iterator. /// /// See [`ProgressIterator`](trait.ProgressIterator.html) for method /// documentation. #[cfg_attr(docsrs, doc(cfg(feature = "rayon")))] pub trait ParallelProgressIterator where Self: Sized + ParallelIterator, { /// Wrap an iterator with a custom progress bar. fn progress_with(self, progress: ProgressBar) -> ProgressBarIter; /// Wrap an iterator with an explicit element count. fn progress_count(self, len: u64) -> ProgressBarIter { self.progress_with(ProgressBar::new(len)) } fn progress(self) -> ProgressBarIter where Self: IndexedParallelIterator, { let len = u64::try_from(self.len()).unwrap(); self.progress_count(len) } /// Wrap an iterator with a progress bar and style it. fn progress_with_style(self, style: crate::ProgressStyle) -> ProgressBarIter where Self: IndexedParallelIterator, { let len = u64::try_from(self.len()).unwrap(); let bar = ProgressBar::new(len).with_style(style); self.progress_with(bar) } } impl> ParallelProgressIterator for T { fn progress_with(self, progress: ProgressBar) -> ProgressBarIter { ProgressBarIter { it: self, progress } } } impl> IndexedParallelIterator for ProgressBarIter { fn len(&self) -> usize { self.it.len() } fn drive>(self, consumer: C) -> >::Result { let consumer = ProgressConsumer::new(consumer, self.progress); self.it.drive(consumer) } fn with_producer>( self, callback: CB, ) -> >::Output { return self.it.with_producer(Callback { callback, progress: self.progress, }); struct Callback { callback: CB, progress: ProgressBar, } impl> ProducerCallback for Callback { type Output = CB::Output; fn callback

(self, base: P) -> CB::Output where P: Producer, { let producer = ProgressProducer { base, progress: self.progress, }; self.callback.callback(producer) } } } } struct ProgressProducer { base: T, progress: ProgressBar, } impl> Producer for ProgressProducer

{ type Item = T; type IntoIter = ProgressBarIter; fn into_iter(self) -> Self::IntoIter { ProgressBarIter { it: self.base.into_iter(), progress: self.progress, } } fn min_len(&self) -> usize { self.base.min_len() } fn max_len(&self) -> usize { self.base.max_len() } fn split_at(self, index: usize) -> (Self, Self) { let (left, right) = self.base.split_at(index); ( ProgressProducer { base: left, progress: self.progress.clone(), }, ProgressProducer { base: right, progress: self.progress, }, ) } } struct ProgressConsumer { base: C, progress: ProgressBar, } impl ProgressConsumer { fn new(base: C, progress: ProgressBar) -> Self { ProgressConsumer { base, progress } } } impl> Consumer for ProgressConsumer { type Folder = ProgressFolder; type Reducer = C::Reducer; type Result = C::Result; fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) { let (left, right, reducer) = self.base.split_at(index); ( ProgressConsumer::new(left, self.progress.clone()), ProgressConsumer::new(right, self.progress), reducer, ) } fn into_folder(self) -> Self::Folder { ProgressFolder { base: self.base.into_folder(), progress: self.progress, } } fn full(&self) -> bool { self.base.full() } } impl> UnindexedConsumer for ProgressConsumer { fn split_off_left(&self) -> Self { ProgressConsumer::new(self.base.split_off_left(), self.progress.clone()) } fn to_reducer(&self) -> Self::Reducer { self.base.to_reducer() } } struct ProgressFolder { base: C, progress: ProgressBar, } impl> Folder for ProgressFolder { type Result = C::Result; fn consume(self, item: T) -> Self { self.progress.inc(1); ProgressFolder { base: self.base.consume(item), progress: self.progress, } } fn complete(self) -> C::Result { self.base.complete() } fn full(&self) -> bool { self.base.full() } } impl> ParallelIterator for ProgressBarIter { type Item = S; fn drive_unindexed>(self, consumer: C) -> C::Result { let consumer1 = ProgressConsumer::new(consumer, self.progress.clone()); self.it.drive_unindexed(consumer1) } } #[cfg(test)] mod test { use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use crate::{ParallelProgressIterator, ProgressBar, ProgressBarIter, ProgressStyle}; #[test] fn it_can_wrap_a_parallel_iterator() { let v = vec![1, 2, 3]; fn wrap<'a, T: ParallelIterator>(it: ProgressBarIter) { assert_eq!(it.map(|x| x * 2).collect::>(), vec![2, 4, 6]); } wrap(v.par_iter().progress_count(3)); wrap({ let pb = ProgressBar::new(v.len() as u64); v.par_iter().progress_with(pb) }); wrap({ let style = ProgressStyle::default_bar() .template("{wide_bar:.red} {percent}/100%") .unwrap(); v.par_iter().progress_with_style(style) }); } }