1
- use std:: { ops:: DerefMut , time:: Duration } ;
1
+ use std:: { ops:: DerefMut , process :: ExitStatus , time:: Duration } ;
2
2
3
3
use futures:: future:: try_join3;
4
- use mlua:: { AnyUserData , ExternalError , IntoLua , IntoLuaMulti , Table , UserData , Value } ;
4
+ use mlua:: { AnyUserData , ExternalError , IntoLua , IntoLuaMulti , Table , UserData , UserDataFields , UserDataMethods , Value } ;
5
5
use tokio:: { io:: { self , AsyncBufReadExt , AsyncReadExt , AsyncWriteExt , BufReader , BufWriter } , process:: { ChildStderr , ChildStdin , ChildStdout } , select} ;
6
6
use yazi_binding:: Error ;
7
7
8
8
use super :: Status ;
9
9
use crate :: process:: Output ;
10
10
11
11
pub struct Child {
12
- inner : tokio:: process:: Child ,
13
- stdin : Option < BufWriter < ChildStdin > > ,
14
- stdout : Option < BufReader < ChildStdout > > ,
15
- stderr : Option < BufReader < ChildStderr > > ,
12
+ inner : tokio:: process:: Child ,
13
+ stdin : Option < BufWriter < ChildStdin > > ,
14
+ stdout : Option < BufReader < ChildStdout > > ,
15
+ stderr : Option < BufReader < ChildStderr > > ,
16
+ #[ cfg( windows) ]
17
+ job_handle : Option < std:: os:: windows:: io:: RawHandle > ,
18
+ }
19
+
20
+ #[ cfg( windows) ]
21
+ impl Drop for Child {
22
+ fn drop ( & mut self ) {
23
+ if let Some ( h) = self . job_handle . take ( ) {
24
+ unsafe { windows_sys:: Win32 :: Foundation :: CloseHandle ( h) } ;
25
+ }
26
+ }
16
27
}
17
28
18
29
impl Child {
19
- pub fn new ( mut inner : tokio:: process:: Child ) -> Self {
30
+ pub fn new (
31
+ mut inner : tokio:: process:: Child ,
32
+ #[ cfg( windows) ] job_handle : Option < std:: os:: windows:: io:: RawHandle > ,
33
+ ) -> Self {
20
34
let stdin = inner. stdin . take ( ) . map ( BufWriter :: new) ;
21
35
let stdout = inner. stdout . take ( ) . map ( BufReader :: new) ;
22
36
let stderr = inner. stderr . take ( ) . map ( BufReader :: new) ;
23
- Self { inner, stdin, stdout, stderr }
37
+ Self {
38
+ inner,
39
+ stdin,
40
+ stdout,
41
+ stderr,
42
+ #[ cfg( windows) ]
43
+ job_handle,
44
+ }
24
45
}
25
- }
26
46
27
- impl UserData for Child {
28
- fn add_methods < M : mlua:: UserDataMethods < Self > > ( methods : & mut M ) {
29
- #[ inline]
30
- async fn read_line ( me : & mut Child ) -> ( Option < Vec < u8 > > , u8 ) {
31
- async fn read ( r : Option < impl AsyncBufReadExt + Unpin > ) -> Option < Vec < u8 > > {
32
- let mut buf = Vec :: new ( ) ;
33
- match r?. read_until ( b'\n' , & mut buf) . await {
34
- Ok ( 0 ) | Err ( _) => None ,
35
- Ok ( _) => Some ( buf) ,
36
- }
47
+ pub ( super ) async fn wait ( & mut self ) -> io:: Result < ExitStatus > {
48
+ drop ( self . stdin . take ( ) ) ;
49
+ self . inner . wait ( ) . await
50
+ }
51
+
52
+ pub ( super ) async fn status ( & mut self ) -> io:: Result < ExitStatus > {
53
+ drop ( self . stdin . take ( ) ) ;
54
+ drop ( self . stdout . take ( ) ) ;
55
+ drop ( self . stderr . take ( ) ) ;
56
+ self . inner . wait ( ) . await
57
+ }
58
+
59
+ async fn read_line ( & mut self ) -> ( Option < Vec < u8 > > , u8 ) {
60
+ async fn read ( r : Option < impl AsyncBufReadExt + Unpin > ) -> Option < Vec < u8 > > {
61
+ let mut buf = Vec :: new ( ) ;
62
+ match r?. read_until ( b'\n' , & mut buf) . await {
63
+ Ok ( 0 ) | Err ( _) => None ,
64
+ Ok ( _) => Some ( buf) ,
37
65
}
66
+ }
67
+
68
+ select ! {
69
+ r @ Some ( _) = read( self . stdout. as_mut( ) ) => ( r, 0u8 ) ,
70
+ r @ Some ( _) = read( self . stderr. as_mut( ) ) => ( r, 1u8 ) ,
71
+ else => ( None , 2u8 ) ,
72
+ }
73
+ }
38
74
39
- select ! {
40
- r @ Some ( _) = read( me. stdout. as_mut( ) ) => ( r, 0u8 ) ,
41
- r @ Some ( _) = read( me. stderr. as_mut( ) ) => ( r, 1u8 ) ,
42
- else => ( None , 2u8 ) ,
75
+ pub ( super ) async fn wait_with_output ( mut self ) -> io:: Result < std:: process:: Output > {
76
+ async fn read ( r : & mut Option < impl AsyncBufReadExt + Unpin > ) -> io:: Result < Vec < u8 > > {
77
+ let mut vec = Vec :: new ( ) ;
78
+ if let Some ( r) = r. as_mut ( ) {
79
+ r. read_to_end ( & mut vec) . await ?;
43
80
}
81
+ Ok ( vec)
44
82
}
45
83
84
+ // Ensure stdin is closed so the child isn't stuck waiting on input while the
85
+ // parent is waiting for it to exit.
86
+ drop ( self . stdin . take ( ) ) ;
87
+
88
+ // Drop happens after `try_join` due to <https://github.com/tokio-rs/tokio/issues/4309>
89
+ let mut stdout = self . stdout . take ( ) ;
90
+ let mut stderr = self . stderr . take ( ) ;
91
+
92
+ let result = try_join3 ( self . inner . wait ( ) , read ( & mut stdout) , read ( & mut stderr) ) . await ?;
93
+ Ok ( std:: process:: Output { status : result. 0 , stdout : result. 1 , stderr : result. 2 } )
94
+ }
95
+ }
96
+
97
+ impl UserData for Child {
98
+ fn add_methods < M : UserDataMethods < Self > > ( methods : & mut M ) {
99
+ methods. add_method ( "id" , |_, me, ( ) | Ok ( me. inner . id ( ) ) ) ;
100
+
46
101
methods. add_async_method_mut ( "read" , |_, mut me, len : usize | async move {
47
102
async fn read ( r : Option < impl AsyncBufReadExt + Unpin > , len : usize ) -> Option < Vec < u8 > > {
48
103
let mut r = r?;
@@ -62,14 +117,15 @@ impl UserData for Child {
62
117
} )
63
118
} ) ;
64
119
methods. add_async_method_mut ( "read_line" , |lua, mut me, ( ) | async move {
65
- match read_line ( & mut me ) . await {
120
+ match me . read_line ( ) . await {
66
121
( Some ( b) , event) => ( lua. create_string ( b) ?, event) . into_lua_multi ( & lua) ,
67
122
( None , event) => ( Value :: Nil , event) . into_lua_multi ( & lua) ,
68
123
}
69
124
} ) ;
125
+ // TODO: deprecate this method
70
126
methods. add_async_method_mut ( "read_line_with" , |lua, mut me, options : Table | async move {
71
127
let timeout = Duration :: from_millis ( options. raw_get ( "timeout" ) ?) ;
72
- let Ok ( result) = tokio:: time:: timeout ( timeout, read_line ( & mut me ) ) . await else {
128
+ let Ok ( result) = tokio:: time:: timeout ( timeout, me . read_line ( ) ) . await else {
73
129
return ( Value :: Nil , 3u8 ) . into_lua_multi ( & lua) ;
74
130
} ;
75
131
match result {
@@ -98,38 +154,21 @@ impl UserData for Child {
98
154
} ) ;
99
155
100
156
methods. add_async_method_mut ( "wait" , |lua, mut me, ( ) | async move {
101
- drop ( me. stdin . take ( ) ) ;
102
- match me. inner . wait ( ) . await {
157
+ match me. wait ( ) . await {
103
158
Ok ( status) => ( Status :: new ( status) , Value :: Nil ) . into_lua_multi ( & lua) ,
104
159
Err ( e) => ( Value :: Nil , Error :: Io ( e) ) . into_lua_multi ( & lua) ,
105
160
}
106
161
} ) ;
107
162
methods. add_async_function ( "wait_with_output" , |lua, ud : AnyUserData | async move {
108
- async fn read_to_end ( r : & mut Option < impl AsyncBufReadExt + Unpin > ) -> io:: Result < Vec < u8 > > {
109
- let mut vec = Vec :: new ( ) ;
110
- if let Some ( r) = r. as_mut ( ) {
111
- r. read_to_end ( & mut vec) . await ?;
112
- }
113
- Ok ( vec)
163
+ match ud. take :: < Self > ( ) ?. wait_with_output ( ) . await {
164
+ Ok ( output) => ( Output :: new ( output) , Value :: Nil ) . into_lua_multi ( & lua) ,
165
+ Err ( e) => ( Value :: Nil , Error :: Io ( e) ) . into_lua_multi ( & lua) ,
114
166
}
115
-
116
- let mut me = ud. take :: < Self > ( ) ?;
117
- let mut stdout_pipe = me. stdout . take ( ) ;
118
- let mut stderr_pipe = me. stderr . take ( ) ;
119
-
120
- let stdout_fut = read_to_end ( & mut stdout_pipe) ;
121
- let stderr_fut = read_to_end ( & mut stderr_pipe) ;
122
-
123
- drop ( me. stdin . take ( ) ) ;
124
- let result = try_join3 ( me. inner . wait ( ) , stdout_fut, stderr_fut) . await ;
125
- drop ( stdout_pipe) ;
126
- drop ( stderr_pipe) ;
127
-
128
- match result {
129
- Ok ( ( status, stdout, stderr) ) => {
130
- ( Output :: new ( std:: process:: Output { status, stdout, stderr } ) , Value :: Nil )
131
- . into_lua_multi ( & lua)
132
- }
167
+ } ) ;
168
+ methods. add_async_method_mut ( "try_wait" , |lua, mut me, ( ) | async move {
169
+ match me. inner . try_wait ( ) {
170
+ Ok ( Some ( status) ) => ( Status :: new ( status) , Value :: Nil ) . into_lua_multi ( & lua) ,
171
+ Ok ( None ) => ( Value :: Nil , Value :: Nil ) . into_lua_multi ( & lua) ,
133
172
Err ( e) => ( Value :: Nil , Error :: Io ( e) ) . into_lua_multi ( & lua) ,
134
173
}
135
174
} ) ;
0 commit comments