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}