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}