command_ext/wrap/mod.rs
1use std::{
2 ffi::OsStr,
3 path::Path,
4 process::{Child, Command, CommandArgs, CommandEnvs, ExitStatus, Output, Stdio},
5};
6
7pub trait HasCommand {
8 fn command(&self) -> &Command;
9 fn command_mut(&mut self) -> &mut Command;
10}
11
12pub trait CommandWrap: HasCommand {
13 #[allow(unused)]
14 #[inline(always)]
15 /// Called when an arg is added using [`arg`]
16 fn on_arg<S: AsRef<OsStr>>(&mut self, arg: S) {}
17
18 #[allow(unused)]
19 #[inline(always)]
20 /// Called when args are added using [`args`]
21 fn on_args<I, S>(&mut self, args: I)
22 where
23 I: IntoIterator<Item = S>,
24 S: AsRef<OsStr>,
25 {
26 }
27
28 #[allow(unused)]
29 #[inline(always)]
30 /// Called when environment variables are configured using [`env`]
31 fn on_env<K, V>(&mut self, key: K, val: V)
32 where
33 K: AsRef<OsStr>,
34 V: AsRef<OsStr>,
35 {
36 }
37
38 #[allow(unused)]
39 #[inline(always)]
40 /// Called when environment variables are configured using [`envs`]
41 fn on_envs<'a, I, K, V>(&mut self, vars: I)
42 where
43 I: IntoIterator<Item = &'a (K, V)>,
44 K: AsRef<OsStr> + 'a,
45 V: AsRef<OsStr> + 'a,
46 {
47 }
48
49 #[allow(unused)]
50 #[inline(always)]
51 /// Called when environment variables are removed using [`env_remove`]
52 fn on_env_remove<K: AsRef<OsStr>>(&mut self, key: K) {}
53
54 #[allow(unused)]
55 #[inline(always)]
56 /// Called when environment variables are cleared using [`env_clear`]
57 fn on_env_clear(&mut self) {}
58
59 #[allow(unused)]
60 #[inline(always)]
61 /// Called when the current directory is set using [`current_dir`]
62 fn on_current_dir<P: AsRef<Path>>(&mut self, dir: P) {}
63
64 #[allow(unused)]
65 #[inline(always)]
66 /// Called when stdin is set using [`stdin`]
67 fn on_stdin(&mut self, cfg: &Stdio) {}
68
69 #[allow(unused)]
70 #[inline(always)]
71 /// Called when stdout is set using [`stdout`]
72 fn on_stdout(&mut self, cfg: &Stdio) {}
73
74 #[allow(unused)]
75 #[inline(always)]
76 /// Called when stderr is set using [`stderr`]
77 fn on_stderr(&mut self, cfg: &Stdio) {}
78
79 #[allow(unused)]
80 #[inline(always)]
81 /// Called when the child process is spawned using [`spawn`]
82 fn on_spawn(&mut self) {}
83
84 #[allow(unused)]
85 #[inline(always)]
86 /// Called when output is created using [`output`]
87 fn on_output(&mut self) {}
88
89 #[allow(unused)]
90 #[inline(always)]
91 /// Called when status is obtained using [`status`]
92 fn on_status(&mut self) {}
93
94 #[allow(unused)]
95 #[inline(always)]
96 /// Called when the child process is spawned using [`spawn`]
97 fn after_spawn(&mut self, child: &std::io::Result<Child>) {}
98
99 #[allow(unused)]
100 #[inline(always)]
101 /// Called when output is created using [`output`]
102 fn after_output(&mut self, output: &std::io::Result<Output>) {}
103
104 #[allow(unused)]
105 #[inline(always)]
106 /// Called when status is obtained using [`status`]
107 fn after_status(&mut self, status: &std::io::Result<ExitStatus>) {}
108
109 /// Adds an argument to pass to the program.
110 ///
111 /// Only one argument can be passed per use. So instead of:
112 ///
113 /// ```no_run
114 /// # std::process::Command::new("sh")
115 /// .arg("-C /path/to/repo")
116 /// # ;
117 /// ```
118 ///
119 /// usage would be:
120 ///
121 /// ```no_run
122 /// # std::process::Command::new("sh")
123 /// .arg("-C")
124 /// .arg("/path/to/repo")
125 /// # ;
126 /// ```
127 ///
128 /// To pass multiple arguments see [`args`].
129 ///
130 /// [`args`]: Command::args
131 ///
132 /// Note that the argument is not passed through a shell, but given
133 /// literally to the program. This means that shell syntax like quotes,
134 /// escaped characters, word splitting, glob patterns, substitution, etc.
135 /// have no effect.
136 ///
137 /// # Examples
138 ///
139 /// Basic usage:
140 ///
141 /// ```no_run
142 /// use std::process::Command;
143 ///
144 /// Command::new("ls")
145 /// .arg("-l")
146 /// .arg("-a")
147 /// .spawn()
148 /// .expect("ls command failed to start");
149 /// ```
150 fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
151 self.on_arg(&arg);
152 self.command_mut().arg(arg);
153 self
154 }
155
156 /// Adds multiple arguments to pass to the program.
157 ///
158 /// To pass a single argument see [`arg`].
159 ///
160 /// [`arg`]: Command::arg
161 ///
162 /// Note that the arguments are not passed through a shell, but given
163 /// literally to the program. This means that shell syntax like quotes,
164 /// escaped characters, word splitting, glob patterns, substitution, etc.
165 /// have no effect.
166 ///
167 /// # Examples
168 ///
169 /// Basic usage:
170 ///
171 /// ```no_run
172 /// use std::process::Command;
173 ///
174 /// Command::new("ls")
175 /// .args(["-l", "-a"])
176 /// .spawn()
177 /// .expect("ls command failed to start");
178 /// ```
179 fn args<I, S>(&mut self, args: I) -> &mut Self
180 where
181 I: IntoIterator<Item = S>,
182 S: AsRef<OsStr>,
183 {
184 self.command_mut().args(args);
185 self
186 }
187
188 /// Inserts or updates an explicit environment variable mapping.
189 ///
190 /// This method allows you to add an environment variable mapping to the spawned process or
191 /// overwrite a previously set value. You can use [`Command::envs`] to set multiple environment
192 /// variables simultaneously.
193 ///
194 /// Child processes will inherit environment variables from their parent process by default.
195 /// Environment variables explicitly set using [`Command::env`] take precedence over inherited
196 /// variables. You can disable environment variable inheritance entirely using
197 /// [`Command::env_clear`] or for a single key using [`Command::env_remove`].
198 ///
199 /// Note that environment variable names are case-insensitive (but
200 /// case-preserving) on Windows and case-sensitive on all other platforms.
201 ///
202 /// # Examples
203 ///
204 /// Basic usage:
205 ///
206 /// ```no_run
207 /// use std::process::Command;
208 ///
209 /// Command::new("ls")
210 /// .env("PATH", "/bin")
211 /// .spawn()
212 /// .expect("ls command failed to start");
213 /// ```
214 fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
215 where
216 K: AsRef<OsStr>,
217 V: AsRef<OsStr>,
218 {
219 self.on_env(&key, &val);
220 self.command_mut().env(key, val);
221 self
222 }
223
224 /// Inserts or updates multiple explicit environment variable mappings.
225 ///
226 /// This method allows you to add multiple environment variable mappings to the spawned process
227 /// or overwrite previously set values. You can use [`Command::env`] to set a single environment
228 /// variable.
229 ///
230 /// Child processes will inherit environment variables from their parent process by default.
231 /// Environment variables explicitly set using [`Command::envs`] take precedence over inherited
232 /// variables. You can disable environment variable inheritance entirely using
233 /// [`Command::env_clear`] or for a single key using [`Command::env_remove`].
234 ///
235 /// Note that environment variable names are case-insensitive (but case-preserving) on Windows
236 /// and case-sensitive on all other platforms.
237 ///
238 /// # Examples
239 ///
240 /// Basic usage:
241 ///
242 /// ```no_run
243 /// use std::process::{Command, Stdio};
244 /// use std::env;
245 /// use std::collections::HashMap;
246 ///
247 /// let filtered_env : HashMap<String, String> =
248 /// env::vars().filter(|&(ref k, _)|
249 /// k == "TERM" || k == "TZ" || k == "LANG" || k == "PATH"
250 /// ).collect();
251 ///
252 /// Command::new("printenv")
253 /// .stdin(Stdio::null())
254 /// .stdout(Stdio::inherit())
255 /// .env_clear()
256 /// .envs(&filtered_env)
257 /// .spawn()
258 /// .expect("printenv failed to start");
259 /// ```
260 fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
261 where
262 I: IntoIterator<Item = (K, V)>,
263 K: AsRef<OsStr>,
264 V: AsRef<OsStr>,
265 {
266 let vars = vars.into_iter().collect::<Vec<_>>();
267 self.on_envs(&vars);
268 self.command_mut().envs(vars);
269 self
270 }
271
272 /// Removes an explicitly set environment variable and prevents inheriting it from a parent
273 /// process.
274 ///
275 /// This method will remove the explicit value of an environment variable set via
276 /// [`Command::env`] or [`Command::envs`]. In addition, it will prevent the spawned child
277 /// process from inheriting that environment variable from its parent process.
278 ///
279 /// After calling [`Command::env_remove`], the value associated with its key from
280 /// [`Command::get_envs`] will be [`None`].
281 ///
282 /// To clear all explicitly set environment variables and disable all environment variable
283 /// inheritance, you can use [`Command::env_clear`].
284 ///
285 /// # Examples
286 ///
287 /// Basic usage:
288 ///
289 /// ```no_run
290 /// use std::process::Command;
291 ///
292 /// Command::new("ls")
293 /// .env_remove("PATH")
294 /// .spawn()
295 /// .expect("ls command failed to start");
296 /// ```
297 fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Self {
298 self.on_env_remove(&key);
299 self.command_mut().env_remove(key);
300 self
301 }
302
303 /// Clears all explicitly set environment variables and prevents inheriting any parent process
304 /// environment variables.
305 ///
306 /// This method will remove all explicitly added environment variables set via [`Command::env`]
307 /// or [`Command::envs`]. In addition, it will prevent the spawned child process from inheriting
308 /// any environment variable from its parent process.
309 ///
310 /// After calling [`Command::env_clear`], the iterator from [`Command::get_envs`] will be
311 /// empty.
312 ///
313 /// You can use [`Command::env_remove`] to clear a single mapping.
314 ///
315 /// # Examples
316 ///
317 /// Basic usage:
318 ///
319 /// ```no_run
320 /// use std::process::Command;
321 ///
322 /// Command::new("ls")
323 /// .env_clear()
324 /// .spawn()
325 /// .expect("ls command failed to start");
326 /// ```
327 fn env_clear(&mut self) -> &mut Self {
328 self.on_env_clear();
329 self.command_mut().env_clear();
330 self
331 }
332
333 /// Sets the working directory for the child process.
334 ///
335 /// # Platform-specific behavior
336 ///
337 /// If the program path is relative (e.g., `"./script.sh"`), it's ambiguous
338 /// whether it should be interpreted relative to the parent's working
339 /// directory or relative to `current_dir`. The behavior in this case is
340 /// platform specific and unstable, and it's recommended to use
341 /// [`canonicalize`] to get an absolute program path instead.
342 ///
343 /// # Examples
344 ///
345 /// Basic usage:
346 ///
347 /// ```no_run
348 /// use std::process::Command;
349 ///
350 /// Command::new("ls")
351 /// .current_dir("/bin")
352 /// .spawn()
353 /// .expect("ls command failed to start");
354 /// ```
355 ///
356 /// [`canonicalize`]: crate::fs::canonicalize
357 fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self {
358 self.on_current_dir(&dir);
359 self.command_mut().current_dir(dir);
360 self
361 }
362
363 /// Configuration for the child process's standard input (stdin) handle.
364 ///
365 /// Defaults to [`inherit`] when used with [`spawn`] or [`status`], and
366 /// defaults to [`piped`] when used with [`output`].
367 ///
368 /// [`inherit`]: Stdio::inherit
369 /// [`piped`]: Stdio::piped
370 /// [`spawn`]: Self::spawn
371 /// [`status`]: Self::status
372 /// [`output`]: Self::output
373 ///
374 /// # Examples
375 ///
376 /// Basic usage:
377 ///
378 /// ```no_run
379 /// use std::process::{Command, Stdio};
380 ///
381 /// Command::new("ls")
382 /// .stdin(Stdio::null())
383 /// .spawn()
384 /// .expect("ls command failed to start");
385 /// ```
386 fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
387 let cfg: Stdio = cfg.into();
388 self.on_stdin(&cfg);
389 self.command_mut().stdin(cfg);
390 self
391 }
392
393 /// Configuration for the child process's standard output (stdout) handle.
394 ///
395 /// Defaults to [`inherit`] when used with [`spawn`] or [`status`], and
396 /// defaults to [`piped`] when used with [`output`].
397 ///
398 /// [`inherit`]: Stdio::inherit
399 /// [`piped`]: Stdio::piped
400 /// [`spawn`]: Self::spawn
401 /// [`status`]: Self::status
402 /// [`output`]: Self::output
403 ///
404 /// # Examples
405 ///
406 /// Basic usage:
407 ///
408 /// ```no_run
409 /// use std::process::{Command, Stdio};
410 ///
411 /// Command::new("ls")
412 /// .stdout(Stdio::null())
413 /// .spawn()
414 /// .expect("ls command failed to start");
415 /// ```
416 fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
417 let cfg: Stdio = cfg.into();
418 self.on_stdout(&cfg);
419 self.command_mut().stdout(cfg);
420 self
421 }
422
423 /// Configuration for the child process's standard error (stderr) handle.
424 ///
425 /// Defaults to [`inherit`] when used with [`spawn`] or [`status`], and
426 /// defaults to [`piped`] when used with [`output`].
427 ///
428 /// [`inherit`]: Stdio::inherit
429 /// [`piped`]: Stdio::piped
430 /// [`spawn`]: Self::spawn
431 /// [`status`]: Self::status
432 /// [`output`]: Self::output
433 ///
434 /// # Examples
435 ///
436 /// Basic usage:
437 ///
438 /// ```no_run
439 /// use std::process::{Command, Stdio};
440 ///
441 /// Command::new("ls")
442 /// .stderr(Stdio::null())
443 /// .spawn()
444 /// .expect("ls command failed to start");
445 /// ```
446 fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
447 let cfg: Stdio = cfg.into();
448 self.on_stderr(&cfg);
449 self.command_mut().stderr(cfg);
450 self
451 }
452
453 /// Executes the command as a child process, returning a handle to it.
454 ///
455 /// By default, stdin, stdout and stderr are inherited from the parent.
456 ///
457 /// # Examples
458 ///
459 /// Basic usage:
460 ///
461 /// ```no_run
462 /// use std::process::Command;
463 ///
464 /// Command::new("ls")
465 /// .spawn()
466 /// .expect("ls command failed to start");
467 /// ```
468 fn spawn(&mut self) -> std::io::Result<Child> {
469 self.on_spawn();
470 let child = self.command_mut().spawn();
471 self.after_spawn(&child);
472 child
473 }
474
475 /// Executes the command as a child process, waiting for it to finish and
476 /// collecting all of its output.
477 ///
478 /// By default, stdout and stderr are captured (and used to provide the
479 /// resulting output). Stdin is not inherited from the parent and any
480 /// attempt by the child process to read from the stdin stream will result
481 /// in the stream immediately closing.
482 ///
483 /// # Examples
484 ///
485 /// ```should_panic
486 /// use std::process::Command;
487 /// use std::io::{self, Write};
488 /// let output = Command::new("/bin/cat")
489 /// .arg("file.txt")
490 /// .output()
491 /// .expect("failed to execute process");
492 ///
493 /// println!("status: {}", output.status);
494 /// io::stdout().write_all(&output.stdout).unwrap();
495 /// io::stderr().write_all(&output.stderr).unwrap();
496 ///
497 /// assert!(output.status.success());
498 /// ```
499 fn output(&mut self) -> std::io::Result<Output> {
500 self.on_output();
501 let output = self.command_mut().output();
502 self.after_output(&output);
503 output
504 }
505
506 /// Executes a command as a child process, waiting for it to finish and
507 /// collecting its status.
508 ///
509 /// By default, stdin, stdout and stderr are inherited from the parent.
510 ///
511 /// # Examples
512 ///
513 /// ```should_panic
514 /// use std::process::Command;
515 ///
516 /// let status = Command::new("/bin/cat")
517 /// .arg("file.txt")
518 /// .status()
519 /// .expect("failed to execute process");
520 ///
521 /// println!("process finished with: {status}");
522 ///
523 /// assert!(status.success());
524 /// ```
525 fn status(&mut self) -> std::io::Result<ExitStatus> {
526 self.on_status();
527 let status = self.command_mut().status();
528 self.after_status(&status);
529 status
530 }
531
532 /// Returns the path to the program that was given to [`Command::new`].
533 ///
534 /// # Examples
535 ///
536 /// ```
537 /// use std::process::Command;
538 ///
539 /// let cmd = Command::new("echo");
540 /// assert_eq!(cmd.get_program(), "echo");
541 /// ```
542 fn get_program(&self) -> &OsStr {
543 self.command().get_program()
544 }
545
546 /// Returns an iterator of the arguments that will be passed to the program.
547 ///
548 /// This does not include the path to the program as the first argument;
549 /// it only includes the arguments specified with [`Command::arg`] and
550 /// [`Command::args`].
551 ///
552 /// # Examples
553 ///
554 /// ```
555 /// use std::ffi::OsStr;
556 /// use std::process::Command;
557 ///
558 /// let mut cmd = Command::new("echo");
559 /// cmd.arg("first").arg("second");
560 /// let args: Vec<&OsStr> = cmd.get_args().collect();
561 /// assert_eq!(args, &["first", "second"]);
562 /// ```
563 fn get_args(&self) -> CommandArgs<'_> {
564 self.command().get_args()
565 }
566
567 /// Returns an iterator of the environment variables explicitly set for the child process.
568 ///
569 /// Environment variables explicitly set using [`Command::env`], [`Command::envs`], and
570 /// [`Command::env_remove`] can be retrieved with this method.
571 ///
572 /// Note that this output does not include environment variables inherited from the parent
573 /// process.
574 ///
575 /// Each element is a tuple key/value pair `(&OsStr, Option<&OsStr>)`. A [`None`] value
576 /// indicates its key was explicitly removed via [`Command::env_remove`]. The associated key for
577 /// the [`None`] value will no longer inherit from its parent process.
578 ///
579 /// An empty iterator can indicate that no explicit mappings were added or that
580 /// [`Command::env_clear`] was called. After calling [`Command::env_clear`], the child process
581 /// will not inherit any environment variables from its parent process.
582 ///
583 /// # Examples
584 ///
585 /// ```
586 /// use std::ffi::OsStr;
587 /// use std::process::Command;
588 ///
589 /// let mut cmd = Command::new("ls");
590 /// cmd.env("TERM", "dumb").env_remove("TZ");
591 /// let envs: Vec<(&OsStr, Option<&OsStr>)> = cmd.get_envs().collect();
592 /// assert_eq!(envs, &[
593 /// (OsStr::new("TERM"), Some(OsStr::new("dumb"))),
594 /// (OsStr::new("TZ"), None)
595 /// ]);
596 /// ```
597 fn get_envs(&self) -> CommandEnvs<'_> {
598 self.command().get_envs()
599 }
600
601 /// Returns the working directory for the child process.
602 ///
603 /// This returns [`None`] if the working directory will not be changed.
604 ///
605 /// # Examples
606 ///
607 /// ```
608 /// use std::path::Path;
609 /// use std::process::Command;
610 ///
611 /// let mut cmd = Command::new("ls");
612 /// assert_eq!(cmd.get_current_dir(), None);
613 /// cmd.current_dir("/bin");
614 /// assert_eq!(cmd.get_current_dir(), Some(Path::new("/bin")));
615 /// ```
616 fn get_current_dir(&self) -> Option<&Path> {
617 self.command().get_current_dir()
618 }
619}