async_std/io/
stdin.rs

1use std::future::Future;
2use std::pin::Pin;
3use std::sync::Mutex;
4
5use crate::future;
6use crate::io::{self, Read};
7use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
8use crate::utils::Context as _;
9
10cfg_unstable! {
11    use once_cell::sync::Lazy;
12    use std::io::Read as _;
13}
14
15/// Constructs a new handle to the standard input of the current process.
16///
17/// This function is an async version of [`std::io::stdin`].
18///
19/// [`std::io::stdin`]: https://doc.rust-lang.org/std/io/fn.stdin.html
20///
21/// ### Note: Windows Portability Consideration
22///
23/// When operating in a console, the Windows implementation of this stream does not support
24/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
25/// an error.
26///
27/// # Examples
28///
29/// ```no_run
30/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
31/// #
32/// use async_std::io;
33///
34/// let stdin = io::stdin();
35/// let mut line = String::new();
36/// stdin.read_line(&mut line).await?;
37/// #
38/// # Ok(()) }) }
39/// ```
40pub fn stdin() -> Stdin {
41    Stdin(Mutex::new(State::Idle(Some(Inner {
42        stdin: std::io::stdin(),
43        line: String::new(),
44        buf: Vec::new(),
45        last_op: None,
46    }))))
47}
48
49/// A handle to the standard input of the current process.
50///
51/// This reader is created by the [`stdin`] function. See its documentation for
52/// more.
53///
54/// ### Note: Windows Portability Consideration
55///
56/// When operating in a console, the Windows implementation of this stream does not support
57/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
58/// an error.
59///
60/// [`stdin`]: fn.stdin.html
61#[derive(Debug)]
62pub struct Stdin(Mutex<State>);
63
64/// A locked reference to the Stdin handle.
65///
66/// This handle implements the [`Read`] traits, and is constructed via the [`Stdin::lock`] method.
67///
68/// [`Read`]: trait.Read.html
69/// [`Stdin::lock`]: struct.Stdin.html#method.lock
70#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
71#[cfg(feature = "unstable")]
72#[derive(Debug)]
73pub struct StdinLock<'a>(std::io::StdinLock<'a>);
74
75#[cfg(feature = "unstable")]
76#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
77unsafe impl Send for StdinLock<'_> {}
78
79/// The state of the asynchronous stdin.
80///
81/// The stdin can be either idle or busy performing an asynchronous operation.
82#[derive(Debug)]
83enum State {
84    /// The stdin is idle.
85    Idle(Option<Inner>),
86
87    /// The stdin is blocked on an asynchronous operation.
88    ///
89    /// Awaiting this operation will result in the new state of the stdin.
90    Busy(JoinHandle<State>),
91}
92
93/// Inner representation of the asynchronous stdin.
94#[derive(Debug)]
95struct Inner {
96    /// The blocking stdin handle.
97    stdin: std::io::Stdin,
98
99    /// The line buffer.
100    line: String,
101
102    /// The write buffer.
103    buf: Vec<u8>,
104
105    /// The result of the last asynchronous operation on the stdin.
106    last_op: Option<Operation>,
107}
108
109/// Possible results of an asynchronous operation on the stdin.
110#[derive(Debug)]
111enum Operation {
112    ReadLine(io::Result<usize>),
113    Read(io::Result<usize>),
114}
115
116impl Stdin {
117    /// Reads a line of input into the specified buffer.
118    ///
119    /// # Examples
120    ///
121    /// ```no_run
122    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
123    /// #
124    /// use async_std::io;
125    ///
126    /// let stdin = io::stdin();
127    /// let mut line = String::new();
128    /// stdin.read_line(&mut line).await?;
129    /// #
130    /// # Ok(()) }) }
131    /// ```
132    pub async fn read_line(&self, buf: &mut String) -> io::Result<usize> {
133        future::poll_fn(|cx| {
134            let state = &mut *self.0.lock().unwrap();
135
136            loop {
137                match state {
138                    State::Idle(opt) => {
139                        let inner = opt.as_mut().unwrap();
140
141                        // Check if the operation has completed.
142                        if let Some(Operation::ReadLine(res)) = inner.last_op.take() {
143                            let n = res?;
144
145                            // Copy the read data into the buffer and return.
146                            buf.push_str(&inner.line);
147                            return Poll::Ready(Ok(n));
148                        } else {
149                            let mut inner = opt.take().unwrap();
150
151                            // Start the operation asynchronously.
152                            *state = State::Busy(spawn_blocking(move || {
153                                inner.line.clear();
154                                let res = inner.stdin.read_line(&mut inner.line);
155                                inner.last_op = Some(Operation::ReadLine(res));
156                                State::Idle(Some(inner))
157                            }));
158                        }
159                    }
160                    // Poll the asynchronous operation the stdin is currently blocked on.
161                    State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx))?,
162                }
163            }
164        })
165        .await
166        .context(|| String::from("could not read line on stdin"))
167    }
168
169    /// Locks this handle to the standard input stream, returning a readable guard.
170    ///
171    /// 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.
172    ///
173    /// # Examples
174    ///
175    /// ```no_run
176    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
177    /// #
178    /// use async_std::io;
179    /// use async_std::prelude::*;
180    ///
181    /// let mut buffer = String::new();
182    ///
183    /// let stdin = io::stdin();
184    /// let mut handle = stdin.lock().await;
185    ///
186    /// handle.read_to_string(&mut buffer).await?;
187    /// #
188    /// # Ok(()) }) }
189    /// ```
190    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
191    #[cfg(any(feature = "unstable", feature = "docs"))]
192    pub async fn lock(&self) -> StdinLock<'static> {
193        static STDIN: Lazy<std::io::Stdin> = Lazy::new(std::io::stdin);
194
195        spawn_blocking(move || StdinLock(STDIN.lock())).await.unwrap()
196    }
197}
198
199impl Read for Stdin {
200    fn poll_read(
201        mut self: Pin<&mut Self>,
202        cx: &mut Context<'_>,
203        buf: &mut [u8],
204    ) -> Poll<io::Result<usize>> {
205        let state = &mut *self.0.lock().unwrap();
206
207        loop {
208            match state {
209                State::Idle(opt) => {
210                    let inner = opt.as_mut().unwrap();
211
212                    // Check if the operation has completed.
213                    if let Some(Operation::Read(res)) = inner.last_op.take() {
214                        let n = res?;
215
216                        // If more data was read than fits into the buffer, let's retry the read
217                        // operation.
218                        if n <= buf.len() {
219                            // Copy the read data into the buffer and return.
220                            buf[..n].copy_from_slice(&inner.buf[..n]);
221                            return Poll::Ready(Ok(n));
222                        }
223                    } else {
224                        let mut inner = opt.take().unwrap();
225
226                        // Set the length of the inner buffer to the length of the provided buffer.
227                        if inner.buf.len() < buf.len() {
228                            inner.buf.reserve(buf.len() - inner.buf.len());
229                        }
230                        unsafe {
231                            inner.buf.set_len(buf.len());
232                        }
233
234                        // Start the operation asynchronously.
235                        *state = State::Busy(spawn_blocking(move || {
236                            let res = std::io::Read::read(&mut inner.stdin, &mut inner.buf);
237                            inner.last_op = Some(Operation::Read(res));
238                            State::Idle(Some(inner))
239                        }));
240                    }
241                }
242                // Poll the asynchronous operation the stdin is currently blocked on.
243                State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx))?,
244            }
245        }
246    }
247}
248
249cfg_unix! {
250    use crate::os::unix::io::{AsRawFd, RawFd};
251
252    impl AsRawFd for Stdin {
253        fn as_raw_fd(&self) -> RawFd {
254            std::io::stdin().as_raw_fd()
255        }
256    }
257}
258
259cfg_windows! {
260    use crate::os::windows::io::{AsRawHandle, RawHandle};
261
262    impl AsRawHandle for Stdin {
263        fn as_raw_handle(&self) -> RawHandle {
264            std::io::stdin().as_raw_handle()
265        }
266    }
267}
268
269#[cfg(feature = "unstable")]
270#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
271impl Read for StdinLock<'_> {
272    fn poll_read(
273        mut self: Pin<&mut Self>,
274        _cx: &mut Context<'_>,
275        buf: &mut [u8],
276    ) -> Poll<io::Result<usize>> {
277        Poll::Ready(self.0.read(buf))
278    }
279}