diff --git a/src/io/copy.rs b/src/io/copy.rs index 753f5e3..8ec3c1a 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -43,6 +43,7 @@ use crate::task::{Context, Poll}; /// # /// # Ok(()) }) } /// ``` +#[cfg(any(feature = "docs", not(feature = "unstable")))] pub async fn copy(reader: &mut R, writer: &mut W) -> io::Result where R: Read + Unpin + ?Sized, @@ -91,3 +92,90 @@ where }; future.await } + +/// Copies the entire contents of a reader into a writer. +/// +/// This function will continuously read data from `reader` and then +/// write it into `writer` in a streaming fashion until `reader` +/// returns EOF. +/// +/// On success, the total number of bytes that were copied from +/// `reader` to `writer` is returned. +/// +/// If you’re wanting to copy the contents of one file to another and you’re +/// working with filesystem paths, see the [`fs::copy`] function. +/// +/// This function is an async version of [`std::io::copy`]. +/// +/// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html +/// [`fs::copy`]: ../fs/fn.copy.html +/// +/// # Errors +/// +/// This function will return an error immediately if any call to `read` or +/// `write` returns an error. All instances of `ErrorKind::Interrupted` are +/// handled by this function and the underlying operation is retried. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::io; +/// +/// let mut reader: &[u8] = b"hello"; +/// let mut writer = io::stdout(); +/// +/// io::copy(&mut reader, &mut writer).await?; +/// # +/// # Ok(()) }) } +/// ``` +#[cfg(all(feature = "unstable", not(feature = "docs")))] +pub async fn copy(reader: R, writer: W) -> io::Result +where + R: Read + Unpin, + W: Write + Unpin, +{ + pin_project! { + struct CopyFuture { + #[pin] + reader: R, + #[pin] + writer: W, + amt: u64, + } + } + + impl Future for CopyFuture + where + R: BufRead, + W: Write + Unpin, + { + type Output = io::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + loop { + let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?; + if buffer.is_empty() { + futures_core::ready!(this.writer.as_mut().poll_flush(cx))?; + return Poll::Ready(Ok(*this.amt)); + } + + let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?; + if i == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + *this.amt += i as u64; + this.reader.as_mut().consume(i); + } + } + } + + let future = CopyFuture { + reader: BufReader::new(reader), + writer, + amt: 0, + }; + future.await +}