Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ pub mod structs {
pub use crate::pad_tail::PadUsing;
#[cfg(feature = "use_alloc")]
pub use crate::peek_nth::PeekNth;
pub use crate::peeking_map_while::PeekingMapWhile;
pub use crate::peeking_take_while::PeekingTakeWhile;
#[cfg(feature = "use_alloc")]
pub use crate::permutations::Permutations;
Expand Down Expand Up @@ -163,6 +164,7 @@ pub use crate::diff::Diff;
#[cfg(feature = "use_alloc")]
pub use crate::kmerge_impl::kmerge_by;
pub use crate::minmax::MinMaxResult;
pub use crate::peeking_map_while::PeekingMapNext;
pub use crate::peeking_take_while::PeekingNext;
pub use crate::process_results_impl::process_results;
pub use crate::repeatn::repeat_n;
Expand Down Expand Up @@ -216,6 +218,7 @@ mod next_array;
mod pad_tail;
#[cfg(feature = "use_alloc")]
mod peek_nth;
mod peeking_map_while;
mod peeking_take_while;
#[cfg(feature = "use_alloc")]
mod permutations;
Expand Down Expand Up @@ -1540,8 +1543,8 @@ pub trait Itertools: Iterator {
/// Return an iterator adaptor that borrows from this iterator and
/// takes items while the closure `accept` returns `true`.
///
/// This adaptor can only be used on iterators that implement `PeekingNext`
/// like `.peekable()`, `put_back` and a few other collection iterators.
/// This adaptor can only be used on iterators that implement [`PeekingNext`]
/// like [`Peekable`](core::iter::Peekable), [`PutBack`] and a few other collection iterators.
///
/// The last and rejected element (first `false`) is still available when
/// `peeking_take_while` is done.
Expand All @@ -1557,10 +1560,20 @@ pub trait Itertools: Iterator {
peeking_take_while::peeking_take_while(self, accept)
}

/// Return an iterator adaptor that borrows from a `Clone`-able iterator
/// Return an iterator adaptor thar borrows from this iterator and takes items while the closure returns [`Either::Left`].
/// If it returns [`Either::Right`] then that value is put back into the iterator (which is implementation dependent) and the iterator returns none
fn peeking_map_while<F, O>(&mut self, accept: F) -> PeekingMapWhile<'_, Self, F>
where
Self: Sized + PeekingMapNext,
F: FnMut(Self::Item) -> Either<O, Self::Item>,
{
peeking_map_while::PeekingMapWhile { iter: self, accept }
}

/// Return an iterator adaptor that borrows from a [`Clone`]-able iterator
/// to only pick off elements while the predicate `accept` returns `true`.
///
/// It uses the `Clone` trait to restore the original iterator so that the
/// It uses the [`Clone`] trait to restore the original iterator so that the
/// last and rejected element (first `false`) is still available when
/// `take_while_ref` is done.
///
Expand Down
86 changes: 86 additions & 0 deletions src/peeking_map_while.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use core::{iter::Peekable, mem};

use either::Either;

use crate::PutBack;

/// An iterator adaptor that takes items while a closure returns [`Either::Left`].
///
/// See [`.peeking_map_while()`](crate::Itertools::peeking_map_while)
/// for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct PeekingMapWhile<'a, I, F> {
pub(crate) iter: &'a mut I,
pub(crate) accept: F,
}

impl<I, F, O> Iterator for PeekingMapWhile<'_, I, F>
where
I: PeekingMapNext,
F: FnMut(I::Item) -> Either<O, I::Item>,
{
type Item = O;

fn next(&mut self) -> Option<O> {
self.iter.peeking_map_next(&mut self.accept)
}
}

/// A trait used in [`PeekingMapWhile`]
pub trait PeekingMapNext: Iterator {
/// If it returns [`Either::Right`] then it should be reinserted to the iterator otherwise it should be returned
fn peeking_map_next<O>(
&mut self,
accept: impl FnMut(Self::Item) -> Either<O, Self::Item>,
) -> Option<O>;
}

impl<I> PeekingMapNext for Peekable<I>
where
I: Iterator,
I::Item: Default,
{
fn peeking_map_next<O>(
&mut self,
mut accept: impl FnMut(I::Item) -> Either<O, I::Item>,
) -> Option<O> {
let dest = self.peek_mut()?;
let item = mem::take(dest);
let out = accept(item);
match out {
Either::Left(out) => {
self.next();
Some(out)
}
Either::Right(item) => {
*dest = item;
None
}
}
}
}

impl<I> PeekingMapNext for PutBack<I>
where
I: Iterator,
{
fn peeking_map_next<O>(
&mut self,
mut accept: impl FnMut(I::Item) -> Either<O, I::Item>,
) -> Option<O> {
match accept(self.next()?) {
Either::Left(out) => Some(out),
Either::Right(item) => {
self.put_back(item);
None
}
}
}
}

impl<'a, I, F> std::fmt::Debug for PeekingMapWhile<'a, I, F>
where
I: Iterator + std::fmt::Debug + 'a,
{
debug_fmt_fields!(PeekingTakeWhile, iter);
}
28 changes: 28 additions & 0 deletions tests/peeking_map_while.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use either::Either;
use itertools::{put_back, Itertools};

#[test]
fn peeking_map_while_peekable() {
let mut r = (0..10).peekable();
let last = r
.peeking_map_while(|x| match x {
0..3 => Either::Left(x * 2),
_ => Either::Right(x),
})
.last();
assert_eq!(last, Some(4));
assert_eq!(r.next(), Some(3));
}

#[test]
fn peeking_map_while_put_back() {
let mut r = put_back(0..10);
let last = r
.peeking_map_while(|x| match x {
0..3 => Either::Left(x * 2),
_ => Either::Right(x),
})
.last();
assert_eq!(last, Some(4));
assert_eq!(r.next(), Some(3));
}