use std::{fmt::Debug, future::Future};
use crate::AsyncIterator;
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Peekable<I>
where
I: AsyncIterator,
{
inner: I,
peeked: Option<Option<I::Item>>,
}
impl<I: AsyncIterator + Debug> Debug for Peekable<I>
where
I::Item: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Peekable")
.field("peeked", &self.peeked)
.field("inner", &self.inner)
.finish()
}
}
impl<I: AsyncIterator + Clone> std::clone::Clone for Peekable<I>
where
I::Item: Clone,
{
fn clone(&self) -> Self {
Self {
peeked: self.peeked.clone(),
inner: self.inner.clone(),
}
}
fn clone_from(&mut self, source: &Self) {
self.inner = source.inner.clone();
self.peeked = source.peeked.clone();
}
}
impl<I> Peekable<I>
where
I: AsyncIterator,
{
#[inline]
pub fn new(inner: I) -> Peekable<I> {
Self {
inner,
peeked: None,
}
}
#[inline]
pub async fn peek(&mut self) -> Option<&I::Item> {
if self.peeked.is_none() {
self.peeked = Some(self.inner.next().await);
}
unsafe { self.peeked.as_mut().unwrap_unchecked() }.as_ref()
}
#[inline]
pub async fn peek_mut(&mut self) -> Option<&mut I::Item> {
if self.peeked.is_none() {
self.peeked = Some(self.inner.next().await);
}
unsafe { self.peeked.as_mut().unwrap_unchecked() }.as_mut()
}
pub async fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option<I::Item> {
match self.next().await {
Some(matched) if func(&matched) => Some(matched),
other => {
assert!(self.peeked.is_none());
self.peeked = Some(other);
None
}
}
}
pub async fn anext_if<F>(&mut self, func: impl FnOnce(&I::Item) -> F) -> Option<I::Item>
where
F: Future<Output = bool>,
{
match self.next().await {
Some(matched) if func(&matched).await => Some(matched),
other => {
assert!(self.peeked.is_none());
self.peeked = Some(other);
None
}
}
}
pub async 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).await
}
}
impl<I> AsyncIterator for Peekable<I>
where
I: AsyncIterator,
{
type Item = <I as AsyncIterator>::Item;
async fn next(&mut self) -> Option<Self::Item> {
match self.peeked.take() {
Some(v) => v,
None => self.inner.next().await,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let peek_len = match self.peeked {
Some(None) => return (0, Some(0)),
Some(Some(_)) => 1,
None => 0,
};
let (lo, hi) = self.inner.size_hint();
let lo = lo.saturating_add(peek_len);
let hi = match hi {
Some(x) => x.checked_add(peek_len),
None => None,
};
(lo, hi)
}
}