termion/async.rs
1use std::io::{self, Read};
2use std::sync::mpsc;
3use std::thread;
4
5use sys::tty::get_tty;
6
7/// Construct an asynchronous handle to the TTY standard input, with a delimiter byte.
8///
9/// This has the same advantages as async_stdin(), but also allows specifying a delimiter byte. The
10/// reader will stop reading after consuming the delimiter byte.
11pub fn async_stdin_until(delimiter: u8) -> AsyncReader {
12 let (send, recv) = mpsc::channel();
13
14 thread::spawn(move || {
15 for i in get_tty().unwrap().bytes() {
16 match i {
17 Ok(byte) => {
18 let end_of_stream = &byte == &delimiter;
19 let send_error = send.send(Ok(byte)).is_err();
20
21 if end_of_stream || send_error {
22 return;
23 }
24 }
25 Err(_) => {
26 return;
27 }
28 }
29 }
30 });
31
32 AsyncReader { recv: recv }
33}
34
35/// Construct an asynchronous handle to the TTY standard input.
36///
37/// This allows you to read from standard input _without blocking_ the current thread.
38/// Specifically, it works by firing up another thread to handle the event stream, which will then
39/// be buffered in a mpsc queue, which will eventually be read by the current thread.
40///
41/// This will not read the piped standard input, but rather read from the TTY device, since reading
42/// asyncronized from piped input would rarely make sense. In other words, if you pipe standard
43/// output from another process, it won't be reflected in the stream returned by this function, as
44/// this represents the TTY device, and not the piped standard input.
45pub fn async_stdin() -> AsyncReader {
46 let (send, recv) = mpsc::channel();
47
48 thread::spawn(move || {
49 for i in get_tty().unwrap().bytes() {
50 if send.send(i).is_err() {
51 return;
52 }
53 }
54 });
55
56 AsyncReader { recv: recv }
57}
58
59/// An asynchronous reader.
60///
61/// This acts as any other stream, with the exception that reading from it won't block. Instead,
62/// the buffer will only be partially updated based on how much the internal buffer holds.
63pub struct AsyncReader {
64 /// The underlying mpsc receiver.
65 recv: mpsc::Receiver<io::Result<u8>>,
66}
67
68// FIXME: Allow constructing an async reader from an arbitrary stream.
69
70impl Read for AsyncReader {
71 /// Read from the byte stream.
72 ///
73 /// This will never block, but try to drain the event queue until empty. If the total number of
74 /// bytes written is lower than the buffer's length, the event queue is empty or that the event
75 /// stream halted.
76 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
77 let mut total = 0;
78
79 loop {
80 if total >= buf.len() {
81 break;
82 }
83
84 match self.recv.try_recv() {
85 Ok(Ok(b)) => {
86 buf[total] = b;
87 total += 1;
88 }
89 Ok(Err(e)) => return Err(e),
90 Err(_) => break,
91 }
92 }
93
94 Ok(total)
95 }
96}
97
98#[cfg(test)]
99mod test {
100 use super::*;
101 use std::io::Read;
102
103 #[test]
104 fn test_async_stdin() {
105 let stdin = async_stdin();
106 stdin.bytes().next();
107 }
108}