command_group/stdlib.rs
1//! Implementation of process group extensions for the
2//! standard library’s [`Command` type](std::process::Command).
3
4use std::{
5 io::Result,
6 process::{Command, ExitStatus, Output},
7};
8
9use crate::{builder::CommandGroupBuilder, GroupChild};
10
11#[doc(inline)]
12pub use erased::ErasedChild;
13
14#[cfg(target_family = "windows")]
15mod windows;
16
17#[cfg(target_family = "unix")]
18mod unix;
19
20pub(crate) mod child;
21pub(crate) mod erased;
22
23/// Extensions for [`Command`](std::process::Command) adding support for process groups.
24pub trait CommandGroup {
25 /// Executes the command as a child process group, returning a handle to it.
26 ///
27 /// By default, stdin, stdout and stderr are inherited from the parent.
28 ///
29 /// On Windows, this creates a job object instead of a POSIX process group.
30 ///
31 /// # Examples
32 ///
33 /// Basic usage:
34 ///
35 /// ```no_run
36 /// use std::process::Command;
37 /// use command_group::CommandGroup;
38 ///
39 /// Command::new("ls")
40 /// .group_spawn()
41 /// .expect("ls command failed to start");
42 /// ```
43 fn group_spawn(&mut self) -> Result<GroupChild> {
44 self.group().spawn()
45 }
46
47 /// Converts the implementor into a [`CommandGroupBuilder`](crate::CommandGroupBuilder), which can be used to
48 /// set flags that are not available on the `Command` type.
49 fn group(&mut self) -> CommandGroupBuilder<std::process::Command>;
50
51 /// Executes the command as a child process group, waiting for it to finish and
52 /// collecting all of its output.
53 ///
54 /// By default, stdout and stderr are captured (and used to provide the
55 /// resulting output). Stdin is not inherited from the parent and any
56 /// attempt by the child process to read from the stdin stream will result
57 /// in the stream immediately closing.
58 ///
59 /// On Windows, this creates a job object instead of a POSIX process group.
60 ///
61 /// # Examples
62 ///
63 /// ```should_panic
64 /// use std::process::Command;
65 /// use std::io::{self, Write};
66 /// use command_group::CommandGroup;
67 ///
68 /// let output = Command::new("/bin/cat")
69 /// .arg("file.txt")
70 /// .group_output()
71 /// .expect("failed to execute process");
72 ///
73 /// println!("status: {}", output.status);
74 /// io::stdout().write_all(&output.stdout).unwrap();
75 /// io::stderr().write_all(&output.stderr).unwrap();
76 ///
77 /// assert!(output.status.success());
78 /// ```
79 fn group_output(&mut self) -> Result<Output> {
80 self.group_spawn()
81 .and_then(|child| child.wait_with_output())
82 }
83
84 /// Executes a command as a child process group, waiting for it to finish and
85 /// collecting its status.
86 ///
87 /// By default, stdin, stdout and stderr are inherited from the parent.
88 ///
89 /// On Windows, this creates a job object instead of a POSIX process group.
90 ///
91 /// # Examples
92 ///
93 /// ```should_panic
94 /// use std::process::Command;
95 /// use command_group::CommandGroup;
96 ///
97 /// let status = Command::new("/bin/cat")
98 /// .arg("file.txt")
99 /// .group_status()
100 /// .expect("failed to execute process");
101 ///
102 /// println!("process finished with: {}", status);
103 ///
104 /// assert!(status.success());
105 /// ```
106 fn group_status(&mut self) -> Result<ExitStatus> {
107 self.group_spawn().and_then(|mut child| child.wait())
108 }
109}
110
111impl CommandGroup for Command {
112 fn group(&mut self) -> CommandGroupBuilder<'_, Command> {
113 CommandGroupBuilder::new(self)
114 }
115}