commit 4be8df4d1c029f6831a633f1cd77c875ac31c4fd Author: Nick Fitzgerald Date: Tue May 16 17:37:18 2017 -0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4308d82 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +target/ +**/*.rs.bk +Cargo.lock diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..76aa37f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +sudo: false +language: rust + +rust: + - stable + - beta + - nightly + +cache: cargo + +env: + matrix: + - PROFILE="" + - PROFILE="--release" + +script: + - cargo build $PROFILE --verbose + - cargo test $PROFILE --verbose diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f728096 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +authors = ["Nick Fitzgerald "] +description = "Like `Iterator::take_while`, but calls the predicate on a peeked value. This allows you to use `Iterator::by_ref` and `Iterator::take_while` together, and still get the first value for which the `take_while` predicate returned false after dropping the `by_ref`." +categories = ["development-tools::debugging", "development-tools::ffi"] +keywords = ["iterator", "take_while", "peek", "by_ref"] +license = "Apache-2.0/MIT" +name = "peeking_take_while" +readme = "./README.md" +repository = "https://github.com/fitzgen/peeking_take_while" +version = "0.1.0" + +[badges] +[badges.travis-ci] +repository = "fitzgen/peeking_take_while" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..cb6038e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,123 @@ +//! # `peeking_take_while` +//! +//! Provides the `peeking_take_while` iterator adaptor method. +//! +//! The `peeking_take_while` method is very similar to `take_while`, but behaves +//! differently when used with a borrowed iterator (perhaps returned by +//! `Iterator::by_ref`). +//! +//! `peeking_take_while` peeks at the next item in the iterator and runs the +//! predicate on that peeked item. This avoids consuming the first item yielded +//! by the underlying iterator for which the predicate returns `false`. On the +//! other hand, `take_while` will consume that first item for which the +//! predicate returns `false`, and it will be lost. +//! +//! ``` +//! extern crate peeking_take_while; +//! +//! // Bring the `peeking_take_while` method for peekable iterators into +//! // scope. +//! use peeking_take_while::PeekableExt; +//! +//! # fn main() { +//! // Let's say we have two collections we want to iterate through: `xs` and +//! // `ys`. We want to perform one operation on all the leading contiguous +//! // elements that match some predicate, and a different thing with the rest of +//! // the elements. With the `xs`, we will use the normal `take_while`. With the +//! // `ys`, we will use `peeking_take_while`. +//! +//! let xs: Vec = (0..100).collect(); +//! let ys = xs.clone(); +//! +//! let mut iter_xs = xs.into_iter(); +//! let mut iter_ys = ys.into_iter().peekable(); +//! +//! { +//! // Let's do one thing with all the items that are less than 10. +//! # fn do_things_with(_: T) {} +//! +//! let xs_less_than_ten = iter_xs.by_ref().take_while(|x| *x < 10); +//! for x in xs_less_than_ten { +//! do_things_with(x); +//! } +//! +//! let ys_less_than_ten = iter_ys.by_ref().peeking_take_while(|y| *y < 10); +//! for y in ys_less_than_ten { +//! do_things_with(y); +//! } +//! } +//! +//! // And now we will do some other thing with the items that are greater than +//! // or equal to 10. +//! +//! // ...except, when using plain old `take_while` we lost 10! +//! assert_eq!(iter_xs.next(), Some(11)); +//! +//! // However, when using `peeking_take_while` we did not! Great! +//! assert_eq!(iter_ys.next(), Some(10)); +//! # } +//! ``` + +use std::iter::Peekable; + +/// The iterator returned by `peeking_take_while`. +/// +/// See the [module documentation](./index.html) for details. +pub struct PeekingTakeWhile<'a, I, P> + where I: 'a + Iterator +{ + iter: &'a mut Peekable, + predicate: P, +} + +impl<'a, I, P> Iterator for PeekingTakeWhile<'a, I, P> + where I: Iterator, + I::Item: ::std::fmt::Debug, + P: FnMut(&::Item) -> bool +{ + type Item = ::Item; + + fn next(&mut self) -> Option { + let predicate = &mut self.predicate; + if self.iter.peek().map_or(false, |x| { + println!("PeekingTakeWhile::next:"); + println!(" x = {:?}", x); + let result = !(predicate)(x); + println!(" result = {:?}", result); + result + }) { + None + } else { + self.iter.next() + } + } +} + +/// The `Iterator` extension trait that provides the `peeking_take_while` +/// method. +/// +/// See the [module documentation](./index.html) for details. +pub trait PeekableExt<'a, I>: Iterator + where I: 'a + Iterator +{ + /// The `Iterator` extension trait that provides the `peeking_take_while` + /// method. + /// + /// See the [module documentation](./index.html) for details. + fn peeking_take_while

(&'a mut self, predicate: P) -> PeekingTakeWhile<'a, I, P> + where Self: Sized, + P: FnMut(&::Item) -> bool; +} + +impl<'a, I> PeekableExt<'a, I> for Peekable + where I: 'a + Iterator +{ + fn peeking_take_while

(&'a mut self, predicate: P) -> PeekingTakeWhile + where P: FnMut(&::Item) -> bool + { + PeekingTakeWhile { + iter: self, + predicate: predicate, + } + } +}