1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Implementation of process group extensions for [Tokio](https://tokio.rs)’s
//! asynchronous [`Command` type](::tokio::process::Command).

use std::{
	io::Result,
	process::{ExitStatus, Output},
};

use tokio::process::Command;

use crate::{builder::CommandGroupBuilder, AsyncGroupChild};

#[doc(inline)]
pub use erased::ErasedChild;

#[cfg(target_family = "windows")]
mod windows;

#[cfg(target_family = "unix")]
mod unix;

pub(crate) mod child;
pub(crate) mod erased;

/// Extensions for [`Command`](::tokio::process::Command) adding support for process groups.
///
/// This uses [`async_trait`] for now to provide async methods as a trait.
#[async_trait::async_trait]
pub trait AsyncCommandGroup {
	/// Executes the command as a child process group, returning a handle to it.
	///
	/// By default, stdin, stdout and stderr are inherited from the parent.
	///
	/// On Windows, this creates a job object instead of a POSIX process group.
	///
	/// # Examples
	///
	/// Basic usage:
	///
	/// ```no_run
	/// # #[tokio::main]
	/// # async fn main() {
	/// use tokio::process::Command;
	/// use command_group::AsyncCommandGroup;
	///
	/// Command::new("ls")
	///         .group_spawn()
	///         .expect("ls command failed to start");
	/// # }
	/// ```
	fn group_spawn(&mut self) -> Result<AsyncGroupChild> {
		self.group().spawn()
	}

	/// Converts the implementor into a [`CommandGroupBuilder`](crate::CommandGroupBuilder), which can be used to
	/// set flags that are not available on the `Command` type.
	fn group(&mut self) -> crate::builder::CommandGroupBuilder<tokio::process::Command>;

	/// Executes the command as a child process group, waiting for it to finish and
	/// collecting all of its output.
	///
	/// By default, stdout and stderr are captured (and used to provide the
	/// resulting output). Stdin is not inherited from the parent and any
	/// attempt by the child process to read from the stdin stream will result
	/// in the stream immediately closing.
	///
	/// On Windows, this creates a job object instead of a POSIX process group.
	///
	/// # Examples
	///
	/// ```should_panic
	/// # #[tokio::main]
	/// # async fn main() {
	/// use tokio::process::Command;
	/// use std::io::{self, Write};
	/// use command_group::AsyncCommandGroup;
	///
	/// let output = Command::new("/bin/cat")
	///                      .arg("file.txt")
	///                      .group_output()
	///                      .await
	///                      .expect("failed to execute process");
	///
	/// println!("status: {}", output.status);
	/// io::stdout().write_all(&output.stdout).unwrap();
	/// io::stderr().write_all(&output.stderr).unwrap();
	///
	/// assert!(output.status.success());
	/// # }
	/// ```
	async fn group_output(&mut self) -> Result<Output> {
		let child = self.group_spawn()?;
		child.wait_with_output().await
	}

	/// Executes a command as a child process group, waiting for it to finish and
	/// collecting its status.
	///
	/// By default, stdin, stdout and stderr are inherited from the parent.
	///
	/// On Windows, this creates a job object instead of a POSIX process group.
	///
	/// # Examples
	///
	/// ```should_panic
	/// # #[tokio::main]
	/// # async fn main() {
	/// use tokio::process::Command;
	/// use command_group::AsyncCommandGroup;
	///
	/// let status = Command::new("/bin/cat")
	///                      .arg("file.txt")
	///                      .group_status()
	///                      .await
	///                      .expect("failed to execute process");
	///
	/// println!("process finished with: {}", status);
	///
	/// assert!(status.success());
	/// # }
	/// ```
	async fn group_status(&mut self) -> Result<ExitStatus> {
		let mut child = self.group_spawn()?;
		child.wait().await
	}
}

#[async_trait::async_trait]
impl AsyncCommandGroup for Command {
	fn group<'a>(&'a mut self) -> CommandGroupBuilder<'a, Command> {
		CommandGroupBuilder::new(self)
	}
}