1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use futures_util::{AsyncRead, AsyncReadExt, FutureExt};

use super::AsyncPeekable;
use std::{
  future::Future,
  io,
  pin::Pin,
  task::{Context, Poll},
};

/// Future for the [`peek_to_string`](super::AsyncPeekable::peek_to_string) method.
#[derive(Debug)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct PeekToString<'a, P> {
  peekable: &'a mut AsyncPeekable<P>,
  buf: &'a mut String,
}

impl<P: Unpin> Unpin for PeekToString<'_, P> {}

impl<'a, P: AsyncRead + Unpin> PeekToString<'a, P> {
  pub(super) fn new(peekable: &'a mut AsyncPeekable<P>, buf: &'a mut String) -> Self {
    Self { peekable, buf }
  }
}

impl<A> Future for PeekToString<'_, A>
where
  A: AsyncRead + Unpin,
{
  type Output = io::Result<usize>;

  fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
    let Self { peekable, buf } = &mut *self;
    let s = match core::str::from_utf8(&peekable.buffer) {
      Ok(s) => s,
      Err(_) => {
        return Poll::Ready(Err(io::Error::new(
          io::ErrorKind::InvalidData,
          "stream did not contain valid UTF-8",
        )))
      }
    };

    let original_buf_len = buf.len();
    buf.push_str(s);

    let inbuf = peekable.buffer.len();
    let mut fut = peekable.reader.read_to_string(buf);
    match fut.poll_unpin(cx) {
      Poll::Ready(Ok(read)) => {
        peekable
          .buffer
          .extend_from_slice(&buf.as_bytes()[original_buf_len + inbuf..]);
        Poll::Ready(Ok(read + inbuf))
      }
      Poll::Ready(Err(e)) => {
        peekable
          .buffer
          .extend_from_slice(&buf.as_bytes()[original_buf_len + inbuf..]);
        Poll::Ready(Err(e))
      }
      Poll::Pending => Poll::Pending,
    }
  }
}