diff --git a/README.md b/README.md index 65ee0e4..9003b46 100644 --- a/README.md +++ b/README.md @@ -9,5 +9,11 @@ Peek forward in an iterator as far as you'd like, memory allowing! ![El Chorro, Spain](.github/img/cover.jpg) ```rust -... +let iter = vec![1, 2].into_iter(); +let mut peekable = iter.fully_peekable(); +assert_eq!(peekable.peek(), Some(&1)); +assert_eq!(peekable.next(), Some(1)); +assert_eq!(peekable.peek(), Some(&2)); +assert_eq!(peekable.next(), Some(2)); +assert_eq!(peekable.peek(), None); ``` diff --git a/src/lib.rs b/src/lib.rs index ebc4c09..abcc227 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,26 +1,26 @@ //! This crate contains an iterator which allows you to fully peek forward any number of elements. - use std::collections::VecDeque; +use std::iter::FusedIterator; /// A trait for an interator which allows you to fully peek forward any number of elements. #[derive(Clone, Debug)] -pub struct FullyPeekable { +pub struct FullyPeekableIterator { iter: I, - queue: VecDeque, + queue: VecDeque } -/// Create a new FullyPeekable iterator from an existing iterator. -impl FullyPeekable { - pub(crate) fn new(iter: I) -> FullyPeekable { - FullyPeekable { +/// Create a new fully-peekable iterator from an existing iterator. +impl FullyPeekableIterator { + fn new(iter: I) -> FullyPeekableIterator { + FullyPeekableIterator { iter, - queue: VecDeque::new(), + queue: VecDeque::new() } } } -/// FullyPeekable will return elements that have been previously peeked before advancing. -impl Iterator for FullyPeekable { +/// Implementation of the typical iterator methods on the fully-peekable iterator. +impl Iterator for FullyPeekableIterator { type Item = I::Item; /// Returns the next value which may advance the iterator. @@ -28,26 +28,215 @@ impl Iterator for FullyPeekable { fn next(&mut self) -> Option { self.queue.pop_front().or_else(|| self.iter.next()) } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + todo!() + } + + #[inline] + fn count(self) -> usize { + todo!() + } + + #[inline] + fn last(mut self) -> Option { + todo!() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + todo!() + } } -impl FullyPeekable { +// TODO: Implement `DoubleEndedIterator` for `FullyPeekableIterator`? + +impl ExactSizeIterator for FullyPeekableIterator {} + +impl FusedIterator for FullyPeekableIterator {} + +impl FullyPeekableIterator { + /// Test if the iterator has another element to yield. May advance the underlying iterator. + #[inline] + pub fn has_next(&mut self) -> bool { + self.peek().is_some() + } + + /// Peek forward to an arbitrary element without advancing the iterator. + #[inline] + pub fn lift(&mut self, index: usize) -> Option<&I::Item> { + while self.queue.len() <= index + 1 { + match self.iter.next() { + Some(item) => self.queue.push_back(item), + None => break + } + }; + self.queue.get(index) + } + + /// Peek forward to an arbitrary element without advancing the iterator. + #[inline] + pub fn lift_mut(&mut self, index: usize) -> Option<&mut I::Item> { + while self.queue.len() <= index + 1 { + match self.iter.next() { + Some(item) => self.queue.push_back(item), + None => break + } + }; + self.queue.get_mut(index) + } + + /// Peek forward to the next element without advancing the iterator. #[inline] pub fn peek(&mut self) -> Option<&I::Item> { - let iter = &mut self.iter; - self.queue.get(0) match { + self.lift(0) + } + /// Peek forward to the next element marking it as mutable without advancing the iterator. + #[inline] + pub fn peek_mut(&mut self) -> Option<&mut I::Item> { + self.lift_mut(0) + } + + /// Consume and return the next value of this iterator if a condition is true. + #[inline] + pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option { + match self.next() { + Some(matched) if func(&matched) => Some(matched), + Some(other) => { + self.queue.push_front(other); + None + } + other => None } } + + /// Consume and return the next item if it is equal to `expected`. + #[inline] + pub fn next_if_eq(&mut self, expected: &T) -> Option + where + T: ?Sized, + I::Item: PartialEq, + { + self.next_if(|next| next == expected) + } +} + +/// A trait for an iterator which allows you to fully peek forward any number of elements. +pub trait IntoFullyPeekableIterator +where + I: Iterator, +{ + /// Return a fully peekable iterator. + fn fully_peekable(self) -> FullyPeekableIterator; +} + +/// Add a fully-peekable iterator implementation to any iterator implicitly. +impl IntoFullyPeekableIterator for I +where + I: Iterator, +{ + /// Return a fully peekable iterator. + fn fully_peekable(self) -> FullyPeekableIterator { + FullyPeekableIterator::new(self) + } } #[cfg(test)] mod tests { - use crate::FullyPeekable; + use crate::{FullyPeekableIterator, IntoFullyPeekableIterator}; + + #[test] + fn the_class_returns_elements_like_an_iterator_when_using_next() { + let iter = vec![1, 2].into_iter(); + let mut peekable = FullyPeekableIterator::new(iter); + assert_eq!(peekable.next(), Some(1)); + assert_eq!(peekable.next(), Some(2)); + assert_eq!(peekable.next(), None); + } + + #[test] + fn it_uses_has_next_to_determine_if_there_are_more_elements() { + let iter = vec![1, 2].into_iter(); + let mut peekable = FullyPeekableIterator::new(iter); + assert_eq!(peekable.has_next(), true); + assert_eq!(peekable.next(), Some(1)); + assert_eq!(peekable.has_next(), true); + assert_eq!(peekable.next(), Some(2)); + assert_eq!(peekable.has_next(), false); + assert_eq!(peekable.next(), None); + assert_eq!(peekable.has_next(), false); + } + + #[test] + fn it_can_lift_elements_without_advancing() { + let iter = vec![1, 2].into_iter(); + let mut peekable = FullyPeekableIterator::new(iter); + assert_eq!(peekable.lift(0), Some(&1)); + assert_eq!(peekable.lift(1), Some(&2)); + assert_eq!(peekable.lift(2), None); + assert_eq!(peekable.next(), Some(1)); + assert_eq!(peekable.next(), Some(2)); + assert_eq!(peekable.peek(), None); + } + + #[test] + fn it_can_peek_at_the_next_element_without_advancing() { + let iter = vec![1, 2].into_iter(); + let mut peekable = iter.fully_peekable(); + assert_eq!(peekable.peek(), Some(&1)); + assert_eq!(peekable.next(), Some(1)); + assert_eq!(peekable.peek(), Some(&2)); + assert_eq!(peekable.next(), Some(2)); + assert_eq!(peekable.peek(), None); + assert_eq!(peekable.next(), None); + } + + #[test] + fn it_can_lift_elements_without_advancing_mut() { + let iter = vec![1, 2].into_iter(); + let mut peekable = FullyPeekableIterator::new(iter); + assert_eq!(peekable.lift_mut(0), Some(&mut 1)); + assert_eq!(peekable.lift_mut(1), Some(&mut 2)); + assert_eq!(peekable.lift_mut(2), None); + assert_eq!(peekable.next(), Some(1)); + assert_eq!(peekable.next(), Some(2)); + assert_eq!(peekable.peek(), None); + } + + #[test] + fn it_can_peek_at_the_next_element_without_advancing_mut() { + let iter = vec![1, 2].into_iter(); + let mut peekable = iter.fully_peekable(); + assert_eq!(peekable.peek_mut(), Some(&mut 1)); + assert_eq!(peekable.next(), Some(1)); + assert_eq!(peekable.peek_mut(), Some(&mut 2)); + assert_eq!(peekable.next(), Some(2)); + assert_eq!(peekable.peek_mut(), None); + assert_eq!(peekable.next(), None); + } + + #[test] + fn it_can_return_the_next_element_if_a_predicate_is_true() { + let iter = vec![1, 2].into_iter(); + let mut peekable = iter.fully_peekable(); + assert_eq!(peekable.next_if(|next| next == &0), None); + assert_eq!(peekable.next_if(|next| next == &1), Some(1)); + assert_eq!(peekable.next_if(|next| next == &1), None); + assert_eq!(peekable.next_if(|next| next == &2), Some(2)); + assert_eq!(peekable.has_next(), false); + } #[test] - fn it_works() { - let iter: Vec = vec![1, 2, 3]; - let peekable = FullyPeekable::new(iter.into_iter()); + fn it_can_return_the_next_element_if_it_is_equal_to_a_supplied_value() { + let iter = vec![1, 2].into_iter(); + let mut peekable = iter.fully_peekable(); + assert_eq!(peekable.next_if_eq(&0), None); + assert_eq!(peekable.next_if_eq(&1), Some(1)); + assert_eq!(peekable.next_if_eq(&1), None); + assert_eq!(peekable.next_if_eq(&2), Some(2)); + assert_eq!(peekable.has_next(), false); } }