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)
}
}