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}