Skip to content

Commit

Permalink
chore: a bit more work
Browse files Browse the repository at this point in the history
  • Loading branch information
clintval committed Oct 15, 2023
1 parent 87b3c7e commit cd8e21b
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 18 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
```
223 changes: 206 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,242 @@
//! 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<I: Iterator> {
pub struct FullyPeekableIterator<I: Iterator> {
iter: I,
queue: VecDeque<I::Item>,
queue: VecDeque<I::Item>
}

/// Create a new FullyPeekable iterator from an existing iterator.
impl<I: Iterator> FullyPeekable<I> {
pub(crate) fn new(iter: I) -> FullyPeekable<I> {
FullyPeekable {
/// Create a new fully-peekable iterator from an existing iterator.
impl<I: Iterator> FullyPeekableIterator<I> {
fn new(iter: I) -> FullyPeekableIterator<I> {
FullyPeekableIterator {
iter,
queue: VecDeque::new(),
queue: VecDeque::new()
}
}
}

/// FullyPeekable will return elements that have been previously peeked before advancing.
impl<I: Iterator> Iterator for FullyPeekable<I> {
/// Implementation of the typical iterator methods on the fully-peekable iterator.
impl<I: Iterator> Iterator for FullyPeekableIterator<I> {
type Item = I::Item;

/// Returns the next value which may advance the iterator.
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.queue.pop_front().or_else(|| self.iter.next())
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
todo!()
}

#[inline]
fn count(self) -> usize {
todo!()
}

#[inline]
fn last(mut self) -> Option<I::Item> {
todo!()
}

#[inline]
fn nth(&mut self, n: usize) -> Option<I::Item> {
todo!()
}
}

impl<I: Iterator> FullyPeekable<I> {
// TODO: Implement `DoubleEndedIterator` for `FullyPeekableIterator`?

impl<I: ExactSizeIterator> ExactSizeIterator for FullyPeekableIterator<I> {}

impl<I: FusedIterator> FusedIterator for FullyPeekableIterator<I> {}

impl<I: Iterator> FullyPeekableIterator<I> {

/// 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<I::Item> {
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<T>(&mut self, expected: &T) -> Option<I::Item>
where
T: ?Sized,
I::Item: PartialEq<T>,
{
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<I>
where
I: Iterator,
{
/// Return a fully peekable iterator.
fn fully_peekable(self) -> FullyPeekableIterator<I>;
}

/// Add a fully-peekable iterator implementation to any iterator implicitly.
impl<I, T> IntoFullyPeekableIterator<I> for I
where
I: Iterator<Item = T>,
{
/// Return a fully peekable iterator.
fn fully_peekable(self) -> FullyPeekableIterator<I> {
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<i32> = 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);
}
}

0 comments on commit cd8e21b

Please sign in to comment.