command_group/
tokio.rs

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