mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-19 00:00:09 +02:00
tokio: add AsyncBufReadExt::lines
This commit is contained in:
committed by
Sean McArthur
parent
ab040bb498
commit
6742816e78
+3
-2
@@ -40,7 +40,7 @@ default = [
|
||||
|
||||
codec = ["io", "tokio-codec"]
|
||||
fs = ["tokio-fs"]
|
||||
io = ["bytes", "tokio-io", "memchr"]
|
||||
io = ["bytes", "tokio-io", "tokio-futures", "memchr"]
|
||||
reactor = ["io", "tokio-reactor"]
|
||||
rt-full = [
|
||||
"num_cpus",
|
||||
@@ -83,7 +83,7 @@ tracing-core = { version = "0.1", optional = true }
|
||||
memchr = { version = "2.2", optional = true }
|
||||
|
||||
# Needed for async/await preview support
|
||||
#tokio-futures = { version = "0.2.0", optional = true, path = "../tokio-futures" }
|
||||
tokio-futures = { version = "0.2.0", optional = true, path = "../tokio-futures" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
tokio-uds = { version = "0.3.0", optional = true, path = "../tokio-uds" }
|
||||
@@ -102,3 +102,4 @@ num_cpus = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
time = "0.1"
|
||||
futures-util-preview = "0.3.0-alpha.17"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::io::lines::{lines, Lines};
|
||||
use crate::io::read_line::{read_line, ReadLine};
|
||||
use crate::io::read_until::{read_until, ReadUntil};
|
||||
|
||||
@@ -66,6 +67,34 @@ pub trait AsyncBufReadExt: AsyncBufRead {
|
||||
{
|
||||
read_line(self, buf)
|
||||
}
|
||||
|
||||
/// Returns a stream over the lines of this reader.
|
||||
/// This method is the async equivalent to [`BufRead::lines`](std::io::BufRead::lines).
|
||||
///
|
||||
/// The stream returned from this function will yield instances of
|
||||
/// [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline
|
||||
/// byte (the 0xA byte) or CRLF (0xD, 0xA bytes) at the end.
|
||||
///
|
||||
/// [`io::Result`]: std::io::Result
|
||||
/// [`String`]: String
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Each line of the stream has the same error semantics as [`AsyncBufReadExt::read_line`].
|
||||
///
|
||||
/// [`AsyncBufReadExt::read_line`]: AsyncBufReadExt::read_line
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// unimplemented!();
|
||||
/// ```
|
||||
fn lines(self) -> Lines<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
lines(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: AsyncBufRead + ?Sized> AsyncBufReadExt for R {}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
use super::read_line::read_line_internal;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio_futures::Stream;
|
||||
use tokio_io::AsyncBufRead;
|
||||
|
||||
/// Stream for the [`lines`](crate::io::AsyncBufReadExt::lines) method.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
pub struct Lines<R> {
|
||||
reader: R,
|
||||
buf: String,
|
||||
bytes: Vec<u8>,
|
||||
read: usize,
|
||||
}
|
||||
|
||||
impl<R: Unpin> Unpin for Lines<R> {}
|
||||
|
||||
pub(crate) fn lines<R>(reader: R) -> Lines<R>
|
||||
where
|
||||
R: AsyncBufRead,
|
||||
{
|
||||
Lines {
|
||||
reader,
|
||||
buf: String::new(),
|
||||
bytes: Vec::new(),
|
||||
read: 0,
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: AsyncBufRead> Stream for Lines<R> {
|
||||
type Item = io::Result<String>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let Self {
|
||||
reader,
|
||||
buf,
|
||||
bytes,
|
||||
read,
|
||||
} = unsafe { self.get_unchecked_mut() };
|
||||
let reader = unsafe { Pin::new_unchecked(reader) };
|
||||
let n = ready!(read_line_internal(reader, cx, buf, bytes, read))?;
|
||||
if n == 0 && buf.is_empty() {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
if buf.ends_with('\n') {
|
||||
buf.pop();
|
||||
if buf.ends_with('\r') {
|
||||
buf.pop();
|
||||
}
|
||||
}
|
||||
Poll::Ready(Some(Ok(mem::replace(buf, String::new()))))
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ mod async_buf_read_ext;
|
||||
mod async_read_ext;
|
||||
mod async_write_ext;
|
||||
mod copy;
|
||||
mod lines;
|
||||
mod read;
|
||||
mod read_exact;
|
||||
mod read_line;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#![deny(warnings, rust_2018_idioms)]
|
||||
#![feature(async_await)]
|
||||
|
||||
use futures_util::StreamExt;
|
||||
use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncRead};
|
||||
use tokio_test::assert_ok;
|
||||
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
#[tokio::test]
|
||||
async fn lines() {
|
||||
struct Rd {
|
||||
val: &'static [u8],
|
||||
}
|
||||
|
||||
impl AsyncRead for Rd {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
_: &mut Context<'_>,
|
||||
_: &mut [u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncBufRead for Rd {
|
||||
fn poll_fill_buf<'a>(
|
||||
self: Pin<&'a mut Self>,
|
||||
_: &mut Context<'_>,
|
||||
) -> Poll<io::Result<&'a [u8]>> {
|
||||
Poll::Ready(Ok(self.val))
|
||||
}
|
||||
|
||||
fn consume(mut self: Pin<&mut Self>, amt: usize) {
|
||||
self.val = &self.val[amt..];
|
||||
}
|
||||
}
|
||||
|
||||
let rd = Rd {
|
||||
val: b"hello\r\nworld\n\n",
|
||||
};
|
||||
let mut st = rd.lines();
|
||||
|
||||
let b = assert_ok!(st.next().await.unwrap());
|
||||
assert_eq!(b, "hello");
|
||||
let b = assert_ok!(st.next().await.unwrap());
|
||||
assert_eq!(b, "world");
|
||||
let b = assert_ok!(st.next().await.unwrap());
|
||||
assert_eq!(b, "");
|
||||
assert!(st.next().await.is_none());
|
||||
}
|
||||
Reference in New Issue
Block a user