1
- use crate :: ffi:: OsStr ;
1
+ use crate :: ffi:: { OsStr , OsString } ;
2
2
use crate :: fmt;
3
3
use crate :: io;
4
- use crate :: marker:: PhantomData ;
5
4
use crate :: num:: NonZero ;
6
5
use crate :: path:: Path ;
7
6
use crate :: sys:: fs:: File ;
@@ -16,7 +15,14 @@ pub use crate::ffi::OsString as EnvKey;
16
15
////////////////////////////////////////////////////////////////////////////////
17
16
18
17
pub struct Command {
18
+ program : OsString ,
19
+ args : Vec < OsString > ,
19
20
env : CommandEnv ,
21
+
22
+ cwd : Option < OsString > ,
23
+ stdin : Option < Stdio > ,
24
+ stdout : Option < Stdio > ,
25
+ stderr : Option < Stdio > ,
20
26
}
21
27
22
28
// passed back to std::process with the pipes connected to the child, if any
@@ -27,47 +33,70 @@ pub struct StdioPipes {
27
33
pub stderr : Option < AnonPipe > ,
28
34
}
29
35
30
- // FIXME: This should be a unit struct, so we can always construct it
31
- // The value here should be never used, since we cannot spawn processes.
36
+ #[ derive( Debug ) ]
32
37
pub enum Stdio {
33
38
Inherit ,
34
39
Null ,
35
40
MakePipe ,
41
+ ParentStdout ,
42
+ ParentStderr ,
43
+ #[ allow( dead_code) ] // This variant exists only for the Debug impl
44
+ InheritFile ( File ) ,
36
45
}
37
46
38
47
impl Command {
39
- pub fn new ( _program : & OsStr ) -> Command {
40
- Command { env : Default :: default ( ) }
48
+ pub fn new ( program : & OsStr ) -> Command {
49
+ Command {
50
+ program : program. to_owned ( ) ,
51
+ args : vec ! [ program. to_owned( ) ] ,
52
+ env : Default :: default ( ) ,
53
+ cwd : None ,
54
+ stdin : None ,
55
+ stdout : None ,
56
+ stderr : None ,
57
+ }
41
58
}
42
59
43
- pub fn arg ( & mut self , _arg : & OsStr ) { }
60
+ pub fn arg ( & mut self , arg : & OsStr ) {
61
+ self . args . push ( arg. to_owned ( ) ) ;
62
+ }
44
63
45
64
pub fn env_mut ( & mut self ) -> & mut CommandEnv {
46
65
& mut self . env
47
66
}
48
67
49
- pub fn cwd ( & mut self , _dir : & OsStr ) { }
68
+ pub fn cwd ( & mut self , dir : & OsStr ) {
69
+ self . cwd = Some ( dir. to_owned ( ) ) ;
70
+ }
50
71
51
- pub fn stdin ( & mut self , _stdin : Stdio ) { }
72
+ pub fn stdin ( & mut self , stdin : Stdio ) {
73
+ self . stdin = Some ( stdin) ;
74
+ }
52
75
53
- pub fn stdout ( & mut self , _stdout : Stdio ) { }
76
+ pub fn stdout ( & mut self , stdout : Stdio ) {
77
+ self . stdout = Some ( stdout) ;
78
+ }
54
79
55
- pub fn stderr ( & mut self , _stderr : Stdio ) { }
80
+ pub fn stderr ( & mut self , stderr : Stdio ) {
81
+ self . stderr = Some ( stderr) ;
82
+ }
56
83
57
84
pub fn get_program ( & self ) -> & OsStr {
58
- panic ! ( "unsupported" )
85
+ & self . program
59
86
}
60
87
61
88
pub fn get_args ( & self ) -> CommandArgs < ' _ > {
62
- CommandArgs { _p : PhantomData }
89
+ let mut iter = self . args . iter ( ) ;
90
+ iter. next ( ) ;
91
+ CommandArgs { iter }
63
92
}
64
93
65
94
pub fn get_envs ( & self ) -> CommandEnvs < ' _ > {
66
95
self . env . iter ( )
67
96
}
68
97
69
98
pub fn get_current_dir ( & self ) -> Option < & Path > {
70
- None
99
+ self . cwd . as_ref ( ) . map ( |cs| Path :: new ( cs ) )
71
100
}
72
101
73
102
pub fn spawn (
@@ -91,31 +120,83 @@ impl From<AnonPipe> for Stdio {
91
120
92
121
impl From < io:: Stdout > for Stdio {
93
122
fn from ( _: io:: Stdout ) -> Stdio {
94
- // FIXME: This is wrong.
95
- // Instead, the Stdio we have here should be a unit struct.
96
- panic ! ( "unsupported" )
123
+ Stdio :: ParentStdout
97
124
}
98
125
}
99
126
100
127
impl From < io:: Stderr > for Stdio {
101
128
fn from ( _: io:: Stderr ) -> Stdio {
102
- // FIXME: This is wrong.
103
- // Instead, the Stdio we have here should be a unit struct.
104
- panic ! ( "unsupported" )
129
+ Stdio :: ParentStderr
105
130
}
106
131
}
107
132
108
133
impl From < File > for Stdio {
109
- fn from ( _file : File ) -> Stdio {
110
- // FIXME: This is wrong.
111
- // Instead, the Stdio we have here should be a unit struct.
112
- panic ! ( "unsupported" )
134
+ fn from ( file : File ) -> Stdio {
135
+ Stdio :: InheritFile ( file)
113
136
}
114
137
}
115
138
116
139
impl fmt:: Debug for Command {
117
- fn fmt ( & self , _f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
118
- Ok ( ( ) )
140
+ // show all attributes
141
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
142
+ if f. alternate ( ) {
143
+ let mut debug_command = f. debug_struct ( "Command" ) ;
144
+ debug_command. field ( "program" , & self . program ) . field ( "args" , & self . args ) ;
145
+ if !self . env . is_unchanged ( ) {
146
+ debug_command. field ( "env" , & self . env ) ;
147
+ }
148
+
149
+ if self . cwd . is_some ( ) {
150
+ debug_command. field ( "cwd" , & self . cwd ) ;
151
+ }
152
+
153
+ if self . stdin . is_some ( ) {
154
+ debug_command. field ( "stdin" , & self . stdin ) ;
155
+ }
156
+ if self . stdout . is_some ( ) {
157
+ debug_command. field ( "stdout" , & self . stdout ) ;
158
+ }
159
+ if self . stderr . is_some ( ) {
160
+ debug_command. field ( "stderr" , & self . stderr ) ;
161
+ }
162
+
163
+ debug_command. finish ( )
164
+ } else {
165
+ if let Some ( ref cwd) = self . cwd {
166
+ write ! ( f, "cd {cwd:?} && " ) ?;
167
+ }
168
+ if self . env . does_clear ( ) {
169
+ write ! ( f, "env -i " ) ?;
170
+ // Altered env vars will be printed next, that should exactly work as expected.
171
+ } else {
172
+ // Removed env vars need the command to be wrapped in `env`.
173
+ let mut any_removed = false ;
174
+ for ( key, value_opt) in self . get_envs ( ) {
175
+ if value_opt. is_none ( ) {
176
+ if !any_removed {
177
+ write ! ( f, "env " ) ?;
178
+ any_removed = true ;
179
+ }
180
+ write ! ( f, "-u {} " , key. to_string_lossy( ) ) ?;
181
+ }
182
+ }
183
+ }
184
+ // Altered env vars can just be added in front of the program.
185
+ for ( key, value_opt) in self . get_envs ( ) {
186
+ if let Some ( value) = value_opt {
187
+ write ! ( f, "{}={value:?} " , key. to_string_lossy( ) ) ?;
188
+ }
189
+ }
190
+ if self . program != self . args [ 0 ] {
191
+ write ! ( f, "[{:?}] " , self . program) ?;
192
+ }
193
+ write ! ( f, "{:?}" , self . args[ 0 ] ) ?;
194
+
195
+ for arg in & self . args [ 1 ..] {
196
+ write ! ( f, " {:?}" , arg) ?;
197
+ }
198
+ Ok ( ( ) )
199
+ }
119
200
}
120
201
}
121
202
@@ -217,23 +298,30 @@ impl Process {
217
298
}
218
299
219
300
pub struct CommandArgs < ' a > {
220
- _p : PhantomData < & ' a ( ) > ,
301
+ iter : crate :: slice :: Iter < ' a , OsString > ,
221
302
}
222
303
223
304
impl < ' a > Iterator for CommandArgs < ' a > {
224
305
type Item = & ' a OsStr ;
225
306
fn next ( & mut self ) -> Option < & ' a OsStr > {
226
- None
307
+ self . iter . next ( ) . map ( |os| & * * os )
227
308
}
228
309
fn size_hint ( & self ) -> ( usize , Option < usize > ) {
229
- ( 0 , Some ( 0 ) )
310
+ self . iter . size_hint ( )
230
311
}
231
312
}
232
313
233
- impl < ' a > ExactSizeIterator for CommandArgs < ' a > { }
314
+ impl < ' a > ExactSizeIterator for CommandArgs < ' a > {
315
+ fn len ( & self ) -> usize {
316
+ self . iter . len ( )
317
+ }
318
+ fn is_empty ( & self ) -> bool {
319
+ self . iter . is_empty ( )
320
+ }
321
+ }
234
322
235
323
impl < ' a > fmt:: Debug for CommandArgs < ' a > {
236
324
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
237
- f. debug_list ( ) . finish ( )
325
+ f. debug_list ( ) . entries ( self . iter . clone ( ) ) . finish ( )
238
326
}
239
327
}
0 commit comments