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