1- use std:: { io:: { Error , ErrorKind } , time:: { Duration , Instant } } ;
1+ use std:: { io:: { Error , ErrorKind } , ops :: Deref , time:: { Duration , Instant } } ;
22
33pub struct AsyncStdin {
4+ fd : Fd ,
45 #[ cfg( unix) ]
56 fds : libc:: fd_set ,
67}
78
9+ impl Default for AsyncStdin {
10+ fn default ( ) -> Self {
11+ let fd = Fd :: new ( ) . expect ( "failed to open stdin" ) ;
12+ #[ cfg( unix) ]
13+ {
14+ let mut me = Self { fd, fds : unsafe { std:: mem:: MaybeUninit :: zeroed ( ) . assume_init ( ) } } ;
15+ me. reset ( ) ;
16+ me
17+ }
18+ #[ cfg( windows) ]
19+ {
20+ Self { fd }
21+ }
22+ }
23+ }
24+
825impl AsyncStdin {
926 pub fn read_until < P > ( & mut self , timeout : Duration , predicate : P ) -> ( Vec < u8 > , std:: io:: Result < ( ) > )
1027 where
@@ -21,7 +38,7 @@ impl AsyncStdin {
2138 continue ;
2239 }
2340
24- let b = Self :: read_u8 ( ) ?;
41+ let b = self . read_u8 ( ) ?;
2542 buf. push ( b) ;
2643
2744 if predicate ( b, & buf) {
@@ -36,15 +53,7 @@ impl AsyncStdin {
3653 }
3754}
3855
39- #[ cfg( unix) ]
40- impl Default for AsyncStdin {
41- fn default ( ) -> Self {
42- let mut me = Self { fds : unsafe { std:: mem:: MaybeUninit :: zeroed ( ) . assume_init ( ) } } ;
43- me. reset ( ) ;
44- me
45- }
46- }
47-
56+ // --- Unix
4857#[ cfg( unix) ]
4958impl AsyncStdin {
5059 pub fn poll ( & mut self , timeout : Duration ) -> std:: io:: Result < bool > {
@@ -54,13 +63,7 @@ impl AsyncStdin {
5463 } ;
5564
5665 let result = unsafe {
57- libc:: select (
58- libc:: STDIN_FILENO + 1 ,
59- & mut self . fds ,
60- std:: ptr:: null_mut ( ) ,
61- std:: ptr:: null_mut ( ) ,
62- & mut tv,
63- )
66+ libc:: select ( * self . fd + 1 , & mut self . fds , std:: ptr:: null_mut ( ) , std:: ptr:: null_mut ( ) , & mut tv)
6467 } ;
6568
6669 match result {
@@ -73,9 +76,9 @@ impl AsyncStdin {
7376 }
7477 }
7578
76- pub fn read_u8 ( ) -> std:: io:: Result < u8 > {
79+ pub fn read_u8 ( & mut self ) -> std:: io:: Result < u8 > {
7780 let mut b = 0 ;
78- match unsafe { libc:: read ( libc :: STDIN_FILENO , & mut b as * mut _ as * mut _ , 1 ) } {
81+ match unsafe { libc:: read ( * self . fd , & mut b as * mut _ as * mut _ , 1 ) } {
7982 -1 => Err ( Error :: last_os_error ( ) ) ,
8083 0 => Err ( Error :: new ( ErrorKind :: UnexpectedEof , "unexpected EOF" ) ) ,
8184 _ => Ok ( b) ,
@@ -85,42 +88,31 @@ impl AsyncStdin {
8588 fn reset ( & mut self ) {
8689 unsafe {
8790 libc:: FD_ZERO ( & mut self . fds ) ;
88- libc:: FD_SET ( libc :: STDIN_FILENO , & mut self . fds ) ;
91+ libc:: FD_SET ( * self . fd , & mut self . fds ) ;
8992 }
9093 }
9194}
9295
93- #[ cfg( windows) ]
94- impl Default for AsyncStdin {
95- fn default ( ) -> Self { Self { } }
96- }
97-
96+ // --- Windows
9897#[ cfg( windows) ]
9998impl AsyncStdin {
10099 pub fn poll ( & mut self , timeout : Duration ) -> std:: io:: Result < bool > {
101- use std:: os:: windows:: io:: AsRawHandle ;
102-
103100 use windows_sys:: Win32 :: { Foundation :: { WAIT_FAILED , WAIT_OBJECT_0 } , System :: Threading :: WaitForSingleObject } ;
104101
105- let handle = std:: io:: stdin ( ) . as_raw_handle ( ) ;
106102 let millis = timeout. as_millis ( ) ;
107- match unsafe { WaitForSingleObject ( handle , millis as u32 ) } {
103+ match unsafe { WaitForSingleObject ( * self . fd , millis as u32 ) } {
108104 WAIT_FAILED => Err ( Error :: last_os_error ( ) ) ,
109105 WAIT_OBJECT_0 => Ok ( true ) ,
110106 _ => Ok ( false ) ,
111107 }
112108 }
113109
114- pub fn read_u8 ( ) -> std:: io:: Result < u8 > {
115- use std:: os:: windows:: io:: AsRawHandle ;
116-
110+ pub fn read_u8 ( & mut self ) -> std:: io:: Result < u8 > {
117111 use windows_sys:: Win32 :: Storage :: FileSystem :: ReadFile ;
118112
119113 let mut buf = 0 ;
120114 let mut bytes = 0 ;
121- let success = unsafe {
122- ReadFile ( std:: io:: stdin ( ) . as_raw_handle ( ) , & mut buf, 1 , & mut bytes, std:: ptr:: null_mut ( ) )
123- } ;
115+ let success = unsafe { ReadFile ( * self . fd , & mut buf, 1 , & mut bytes, std:: ptr:: null_mut ( ) ) } ;
124116
125117 if success == 0 {
126118 return Err ( Error :: last_os_error ( ) ) ;
@@ -130,3 +122,74 @@ impl AsyncStdin {
130122 Ok ( buf)
131123 }
132124}
125+
126+ // --- Fd
127+ struct Fd {
128+ #[ cfg( unix) ]
129+ inner : std:: os:: fd:: RawFd ,
130+ #[ cfg( windows) ]
131+ inner : std:: os:: windows:: io:: RawHandle ,
132+ close : bool ,
133+ }
134+
135+ impl Deref for Fd {
136+ #[ cfg( unix) ]
137+ type Target = std:: os:: fd:: RawFd ;
138+ #[ cfg( windows) ]
139+ type Target = std:: os:: windows:: io:: RawHandle ;
140+
141+ fn deref ( & self ) -> & Self :: Target { & self . inner }
142+ }
143+
144+ impl Drop for Fd {
145+ fn drop ( & mut self ) {
146+ #[ cfg( unix) ]
147+ if self . close {
148+ unsafe { libc:: close ( self . inner ) } ;
149+ }
150+ #[ cfg( windows) ]
151+ if self . close {
152+ unsafe { windows_sys:: Win32 :: Foundation :: CloseHandle ( self . inner ) } ;
153+ }
154+ }
155+ }
156+
157+ impl Fd {
158+ #[ cfg( unix) ]
159+ fn new ( ) -> std:: io:: Result < Self > {
160+ use std:: { fs:: OpenOptions , os:: fd:: IntoRawFd } ;
161+
162+ Ok ( if unsafe { libc:: isatty ( libc:: STDIN_FILENO ) } == 1 {
163+ Self { inner : libc:: STDIN_FILENO , close : false }
164+ } else {
165+ Self {
166+ inner : OpenOptions :: new ( ) . read ( true ) . write ( true ) . open ( "/dev/tty" ) ?. into_raw_fd ( ) ,
167+ close : true ,
168+ }
169+ } )
170+ }
171+
172+ #[ cfg( windows) ]
173+ fn new ( ) -> std:: io:: Result < Self > {
174+ use windows_sys:: Win32 :: { Foundation :: { GENERIC_READ , GENERIC_WRITE , INVALID_HANDLE_VALUE } , Storage :: FileSystem :: { CreateFileW , FILE_SHARE_READ , FILE_SHARE_WRITE , OPEN_EXISTING } } ;
175+
176+ let name: Vec < u16 > = "CONIN$\0 " . encode_utf16 ( ) . collect ( ) ;
177+ let result = unsafe {
178+ CreateFileW (
179+ name. as_ptr ( ) ,
180+ GENERIC_READ | GENERIC_WRITE ,
181+ FILE_SHARE_READ | FILE_SHARE_WRITE ,
182+ std:: ptr:: null_mut ( ) ,
183+ OPEN_EXISTING ,
184+ 0 ,
185+ std:: ptr:: null_mut ( ) ,
186+ )
187+ } ;
188+
189+ if result == INVALID_HANDLE_VALUE {
190+ Err ( std:: io:: Error :: last_os_error ( ) )
191+ } else {
192+ Ok ( Self { inner : result, close : true } )
193+ }
194+ }
195+ }
0 commit comments