diff --git a/src/io/mod.rs b/src/io/mod.rs index 9a125b20b..c81d82f95 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -282,9 +282,9 @@ pub use read::Read; pub use repeat::{repeat, Repeat}; pub use seek::Seek; pub use sink::{sink, Sink}; -pub use stderr::{stderr, Stderr}; -pub use stdin::{stdin, Stdin}; -pub use stdout::{stdout, Stdout}; +pub use stderr::{stderr, Stderr, StderrLock}; +pub use stdin::{stdin, Stdin, StdinLock}; +pub use stdout::{stdout, Stdout, StdoutLock}; pub use timeout::timeout; pub use write::Write; diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 76ca5239e..a0d9b7135 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -1,3 +1,4 @@ +use std::io::Write as StdWrite; use std::pin::Pin; use std::sync::Mutex; @@ -5,6 +6,10 @@ use crate::future::Future; use crate::io::{self, Write}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +cfg_unstable! { + use once_cell::sync::Lazy; +} + /// Constructs a new handle to the standard error of the current process. /// /// This function is an async version of [`std::io::stderr`]. @@ -53,6 +58,16 @@ pub fn stderr() -> Stderr { #[derive(Debug)] pub struct Stderr(Mutex); +/// A locked reference to the Stderr handle. +/// This handle implements the [`Write`] traits, and is constructed via the [`Stderr::lock`] method. +/// +/// [`Write`]: trait.Read.html +/// [`Stderr::lock`]: struct.Stderr.html#method.lock +#[derive(Debug)] +pub struct StderrLock<'a>(std::io::StderrLock<'a>); + +unsafe impl Send for StderrLock<'_> {} + /// The state of the asynchronous stderr. /// /// The stderr can be either idle or busy performing an asynchronous operation. @@ -87,6 +102,35 @@ enum Operation { Flush(io::Result<()>), } +impl Stderr { + /// Locks this handle to the standard error stream, returning a writable guard. + /// + /// The lock is released when the returned lock goes out of scope. The returned guard also implements the Write trait for writing data. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::io; + /// use async_std::prelude::*; + /// + /// let stderr = io::stderr(); + /// let mut handle = stderr.lock().await; + /// + /// handle.write_all(b"hello world").await?; + /// # + /// # Ok(()) }) } + /// ``` + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[cfg(any(feature = "unstable", feature = "docs"))] + pub async fn lock(&self) -> StderrLock<'static> { + static STDERR: Lazy = Lazy::new(std::io::stderr); + + spawn_blocking(move || StderrLock(STDERR.lock())).await + } +} + impl Write for Stderr { fn poll_write( mut self: Pin<&mut Self>, @@ -189,3 +233,21 @@ cfg_windows! { } } } + +impl Write for StderrLock<'_> { + fn poll_write( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Poll::Ready(self.0.write(buf)) + } + + fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.0.flush()) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } +} diff --git a/src/io/stdin.rs b/src/io/stdin.rs index c99a88dba..e5bb508d5 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -5,6 +5,10 @@ use crate::future::{self, Future}; use crate::io::{self, Read}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +cfg_unstable! { + use once_cell::sync::Lazy; +} + /// Constructs a new handle to the standard input of the current process. /// /// This function is an async version of [`std::io::stdin`]. @@ -54,6 +58,16 @@ pub fn stdin() -> Stdin { #[derive(Debug)] pub struct Stdin(Mutex); +/// A locked reference to the Stdin handle. +/// This handle implements the [`Read`] traits, and is constructed via the [`Stdin::lock`] method. +/// +/// [`Read`]: trait.Read.html +/// [`Stdin::lock`]: struct.Stdin.html#method.lock +#[derive(Debug)] +pub struct StdinLock<'a>(std::io::StdinLock<'a>); + +unsafe impl Send for StdinLock<'_> {} + /// The state of the asynchronous stdin. /// /// The stdin can be either idle or busy performing an asynchronous operation. @@ -142,6 +156,35 @@ impl Stdin { }) .await } + + /// Locks this handle to the standard input stream, returning a readable guard. + /// + /// The lock is released when the returned lock goes out of scope. The returned guard also implements the Read trait for accessing the underlying data. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::io; + /// use crate::async_std::prelude::*; + /// + /// let mut buffer = String::new(); + /// + /// let stdin = io::stdin(); + /// let mut handle = stdin.lock().await; + /// + /// handle.read_to_string(&mut buffer).await?; + /// # + /// # Ok(()) }) } + /// ``` + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[cfg(any(feature = "unstable", feature = "docs"))] + pub async fn lock(&self) -> StdinLock<'static> { + static STDIN: Lazy = Lazy::new(std::io::stdin); + + spawn_blocking(move || StdinLock(STDIN.lock())).await + } } impl Read for Stdin { @@ -213,3 +256,15 @@ cfg_windows! { } } } + +impl Read for StdinLock<'_> { + fn poll_read( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + use std::io::Read as StdRead; + + Poll::Ready(self.0.read(buf)) + } +} diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 5455466a6..bf22128fc 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -1,3 +1,4 @@ +use std::io::Write as StdWrite; use std::pin::Pin; use std::sync::Mutex; @@ -5,6 +6,10 @@ use crate::future::Future; use crate::io::{self, Write}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +cfg_unstable! { + use once_cell::sync::Lazy; +} + /// Constructs a new handle to the standard output of the current process. /// /// This function is an async version of [`std::io::stdout`]. @@ -53,6 +58,16 @@ pub fn stdout() -> Stdout { #[derive(Debug)] pub struct Stdout(Mutex); +/// A locked reference to the Stderr handle. +/// This handle implements the [`Write`] traits, and is constructed via the [`Stdout::lock`] method. +/// +/// [`Write`]: trait.Read.html +/// [`Stdout::lock`]: struct.Stdout.html#method.lock +#[derive(Debug)] +pub struct StdoutLock<'a>(std::io::StdoutLock<'a>); + +unsafe impl Send for StdoutLock<'_> {} + /// The state of the asynchronous stdout. /// /// The stdout can be either idle or busy performing an asynchronous operation. @@ -87,6 +102,35 @@ enum Operation { Flush(io::Result<()>), } +impl Stdout { + /// Locks this handle to the standard error stream, returning a writable guard. + /// + /// The lock is released when the returned lock goes out of scope. The returned guard also implements the Write trait for writing data. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::io; + /// use async_std::prelude::*; + /// + /// let stdout = io::stdout(); + /// let mut handle = stdout.lock().await; + /// + /// handle.write_all(b"hello world").await?; + /// # + /// # Ok(()) }) } + /// ``` + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[cfg(any(feature = "unstable", feature = "docs"))] + pub async fn lock(&self) -> StdoutLock<'static> { + static STDOUT: Lazy = Lazy::new(std::io::stdout); + + spawn_blocking(move || StdoutLock(STDOUT.lock())).await + } +} + impl Write for Stdout { fn poll_write( mut self: Pin<&mut Self>, @@ -189,3 +233,21 @@ cfg_windows! { } } } + +impl Write for StdoutLock<'_> { + fn poll_write( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Poll::Ready(self.0.write(buf)) + } + + fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.0.flush()) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } +}