mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-08 00:00:13 +02:00
stream: add peek_mut, poll_peek to Peekable (#8262)
This commit is contained in:
@@ -34,6 +34,33 @@ impl<T: Stream> Peekable<T> {
|
|||||||
self.peek.as_ref()
|
self.peek.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Peek at the next item in the stream as a mutable reference.
|
||||||
|
pub async fn peek_mut(&mut self) -> Option<&mut T::Item>
|
||||||
|
where
|
||||||
|
T: Unpin,
|
||||||
|
{
|
||||||
|
if let Some(ref mut it) = self.peek {
|
||||||
|
Some(it)
|
||||||
|
} else {
|
||||||
|
self.peek = self.next().await;
|
||||||
|
self.peek.as_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Poll to peek at the next item in the stream as a mutable reference.
|
||||||
|
pub fn poll_peek(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<&mut T::Item>> {
|
||||||
|
let mut this = self.project();
|
||||||
|
|
||||||
|
if this.peek.is_none() {
|
||||||
|
match this.stream.as_mut().poll_next(cx) {
|
||||||
|
Poll::Ready(item) => *this.peek = item,
|
||||||
|
Poll::Pending => return Poll::Pending,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(this.peek.as_mut())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Stream> Stream for Peekable<T> {
|
impl<T: Stream> Stream for Peekable<T> {
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
use tokio_stream::{self as stream, Stream, StreamExt};
|
use tokio_stream::{self as stream, Stream, StreamExt};
|
||||||
|
use tokio_test::{assert_pending, assert_ready, task};
|
||||||
|
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn size_hint_without_peek() {
|
async fn size_hint_without_peek() {
|
||||||
@@ -121,3 +125,93 @@ async fn size_hint_unbounded_upper() {
|
|||||||
let _ = s.peek().await;
|
let _ = s.peek().await;
|
||||||
assert_eq!(s.size_hint(), (2, None)); // still unbounded after peek
|
assert_eq!(s.size_hint(), (2, None)); // still unbounded after peek
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn peek_does_not_consume() {
|
||||||
|
let mut stream = stream::iter(vec![1, 2, 3]).peekable();
|
||||||
|
|
||||||
|
assert_eq!(stream.peek().await, Some(&1));
|
||||||
|
assert_eq!(stream.peek().await, Some(&1));
|
||||||
|
assert_eq!(stream.next().await, Some(1));
|
||||||
|
assert_eq!(stream.next().await, Some(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn peek_mut_mutates_yielded_item() {
|
||||||
|
let mut stream = stream::iter(vec![1, 2, 3]).peekable();
|
||||||
|
|
||||||
|
if let Some(item) = stream.peek_mut().await {
|
||||||
|
*item += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(stream.next().await, Some(11));
|
||||||
|
assert_eq!(stream.next().await, Some(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn peek_on_empty_stream() {
|
||||||
|
let mut stream = stream::iter(Vec::<i32>::new()).peekable();
|
||||||
|
|
||||||
|
assert_eq!(stream.peek().await, None);
|
||||||
|
assert_eq!(stream.peek_mut().await, None);
|
||||||
|
assert_eq!(stream.next().await, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn poll_peek_does_not_advance_stream() {
|
||||||
|
let mut stream = task::spawn(stream::iter(vec![1, 2, 3]).peekable());
|
||||||
|
|
||||||
|
let first = stream.enter(|cx, s| s.poll_peek(cx).map(|opt| opt.copied()));
|
||||||
|
assert_eq!(assert_ready!(first), Some(1));
|
||||||
|
|
||||||
|
let second = stream.enter(|cx, s| s.poll_peek(cx).map(|opt| opt.copied()));
|
||||||
|
assert_eq!(assert_ready!(second), Some(1));
|
||||||
|
|
||||||
|
let next = stream.enter(|cx, s| s.poll_next(cx));
|
||||||
|
assert_eq!(assert_ready!(next), Some(1));
|
||||||
|
|
||||||
|
let next = stream.enter(|cx, s| s.poll_next(cx));
|
||||||
|
assert_eq!(assert_ready!(next), Some(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn poll_peek_mutates_buffered_item() {
|
||||||
|
let mut stream = task::spawn(stream::iter(vec![1, 2, 3]).peekable());
|
||||||
|
|
||||||
|
stream.enter(|cx, s| {
|
||||||
|
if let Poll::Ready(Some(item)) = s.poll_peek(cx) {
|
||||||
|
*item += 100;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let next = stream.enter(|cx, s| s.poll_next(cx));
|
||||||
|
assert_eq!(assert_ready!(next), Some(101));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PendingOnce {
|
||||||
|
polled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream for PendingOnce {
|
||||||
|
type Item = i32;
|
||||||
|
|
||||||
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<i32>> {
|
||||||
|
if self.polled {
|
||||||
|
Poll::Ready(Some(7))
|
||||||
|
} else {
|
||||||
|
self.polled = true;
|
||||||
|
cx.waker().wake_by_ref();
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn poll_peek_propagates_pending() {
|
||||||
|
let mut stream = task::spawn(PendingOnce { polled: false }.peekable());
|
||||||
|
|
||||||
|
assert_pending!(stream.enter(|cx, s| s.poll_peek(cx).map(|opt| opt.copied())));
|
||||||
|
|
||||||
|
let ready = stream.enter(|cx, s| s.poll_peek(cx).map(|opt| opt.copied()));
|
||||||
|
assert_eq!(assert_ready!(ready), Some(7));
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user