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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
//! Composable wrappers over process::Command.
//!
//! # Quick start
//!
//! ```toml
//! [dependencies]
//! process-wrap = { version = "7.0.0", features = ["std"] }
//! ```
//!
//! ```rust,no_run
//! use std::process::Command;
//! use process_wrap::std::*;
//!
//! let mut command = StdCommandWrap::with_new("watch", |command| { command.arg("ls"); });
//! #[cfg(unix)] { command.wrap(ProcessGroup::leader()); }
//! #[cfg(windows)] { command.wrap(JobObject); }
//! let child = command.spawn()?;
//! let status = child.wait()?;
//! dbg!(status);
//! ```
//!
//! ## Migrating from command-group
//!
//! The above example is equivalent to the `command-group` 5.x usage. To migrate from versions 4.x
//! and below, replace `ProcessGroup::leader()` with `ProcessSession`.
//!
//! # Overview
//!
//! This crate provides a composable set of wrappers over `process::Command` (either from std or
//! from Tokio). It is a more flexible and composable successor to the `command-group` crate, and is
//! meant to be adaptable to additional use cases: for example spawning processes in PTYs currently
//! requires a different crate (such as `pty-process`) which won't function with `command-group`.
//! Implementing a PTY wrapper for `process-wrap` would instead keep the same API and be composable
//! with the existing process group/session implementations.
//!
//! # Usage
//!
//! The core API is [`StdCommandWrap`](std::StdCommandWrap) and [`TokioCommandWrap`](tokio::TokioCommandWrap),
//! which can be constructed either directly from an existing `process::Command`:
//!
//! ```rust
//! use process_wrap::std::*;
//! use std::process::Command;
//! let mut command = Command::new("ls");
//! command.arg("-l");
//! let mut command = StdCommandWrap::from(command);
//! #[cfg(unix)] { command.wrap(ProcessGroup::leader()); }
//! #[cfg(windows)] { command.wrap(JobObject); }
//! ```
//!
//! ...or with a somewhat more ergonomic closure pattern:
//!
//! ```rust
//! use process_wrap::std::*;
//! let mut command = StdCommandWrap::with_new("ls", |command| { command.arg("-l"); });
//! #[cfg(unix)] { command.wrap(ProcessGroup::leader()); }
//! #[cfg(windows)] { command.wrap(JobObject); }
//! ```
//!
//! If targetting a single platform, then a fluent style is possible:
//!
//! ```rust
//! use process_wrap::std::*;
//! StdCommandWrap::with_new("ls", |command| { command.arg("-l"); })
//! .wrap(ProcessGroup::leader());
//! ```
//!
//! The `wrap` method can be called multiple times to add multiple wrappers. The order of the
//! wrappers can be important, as they are applied in the order they are added. The documentation
//! for each wrapper will specify ordering concerns.
//!
//! The `spawn` method is used to spawn the process, after which the `Child` can be interacted with.
//! Methods on `Child` mimic those on `process::Child`, but may be customised by the wrappers. For
//! example, `kill` will send a signal to the process group if the `ProcessGroup` wrapper is used.
//!
//! # KillOnDrop and CreationFlags
//!
//! The options set on an underlying `Command` are not queryable from library or user code. In most
//! cases this is not an issue; however on Windows, the `JobObject` wrapper needs to know the value
//! of `.kill_on_drop()` and any `.creation_flags()` set. The `KillOnDrop` and `CreationFlags` are
//! "shims" that _should_ be used instead of the aforementioned methods on `Command`. They will
//! internally set the values on the `Command` and also store them in the wrapper, so that wrappers
//! are able to access them.
//!
//! In practice:
//!
//! ## Instead of `.kill_on_drop(true)`:
//!
//! ```rust
//! use process_wrap::std::*;
//! let mut command = StdCommandWrap::with_new("ls", |command| { command.arg("-l"); });
//! command.wrap(KillOnDrop);
//! ```
//!
//! ## Instead of `.creation_flags(CREATE_NO_WINDOW)`:
//!
//! ```rust,ignore
//! use process_wrap::std::*;
//! let mut command = StdCommandWrap::with_new("ls", |command| { command.arg("-l"); });
//! command.wrap(CreationFlags(CREATE_NO_WINDOW));
//! ```
//!
//! Internally the `JobObject` wrapper always sets the `CREATE_SUSPENDED` flag, but as it is able to
//! access the `CreationFlags` value it will either resume the process after setting up, or leave it
//! suspended if `CREATE_SUSPENDED` was explicitly set.
//!
//! # Extension
//!
//! The crate is designed to be extensible, and new wrappers can be added by implementing the
//! required traits. The std and Tokio sides are completely separate, due to the different
//! underlying APIs. Of course you can (and should) re-use/share code wherever possible if
//! implementing both.
//!
//! At minimum, you must implement [`StdCommandWrapper`](crate::std::StdCommandWrapper) and/or
//! [`TokioCommandWrapper`](crate::tokio::TokioCommandWrapper). These provide the same functionality
//! (and indeed internally are generated using a common macro), but differ in the exact types used.
//! Here's the most basic impl (shown for Tokio):
//!
//! ```rust
//! use process_wrap::tokio::*;
//! #[derive(Debug)]
//! pub struct YourWrapper;
//! impl TokioCommandWrapper for YourWrapper {}
//! ```
//!
//! The trait provides extension or hook points into the lifecycle of a `Command`:
//!
//! - **`fn extend(&mut self, other: Box<dyn TokioCommandWrapper>)`** is called if
//! `.wrap(YourWrapper)` is done twice. Only one of a wrapper type can exist, so this gives the
//! opportunity to incorporate all or part of the second wrapper instance into the first. By
//! default, this does nothing (ie only the first registered wrapper instance of a type applies).
//!
//! - **`fn pre_spawn(&mut self, command: &mut Command, core: &TokioCommandWrap)`** is called before
//! the command is spawned, and gives mutable access to it. It also gives mutable access to the
//! wrapper instance, so state can be stored if needed. The `core` reference gives access to data
//! from other wrappers; for example, that's how `CreationFlags` on Windows works along with
//! `JobObject`. By default does nothing.
//!
//! - **`fn post_spawn(&mut self, child: &mut tokio::process::Child, core: &TokioCommandWrap)`** is
//! called after spawn, and should be used for any necessary cleanups. It is offered for
//! completeness but is expected to be less used than `wrap_child()`. By default does nothing.
//!
//! - **`fn wrap_child(&mut self, child: Box<dyn TokioChildWrapper>, core: &TokioCommandWrap)`** is
//! called after all `post_spawn()`s have run. If your wrapper needs to override the methods on
//! Child, then it should create an instance of its own type implementing `TokioChildWrapper` and
//! return it here. Child wraps are _in order_: you may end up with a `Foo(Bar(Child))` or a
//! `Bar(Foo(Child))` depending on if `.wrap(Foo).wrap(Bar)` or `.wrap(Bar).wrap(Foo)` was called.
//! If your functionality is order-dependent, make sure to specify so in your documentation! By
//! default does nothing: no wrapping is performed and the input `child` is returned as-is.
//!
//! # Features
//!
//! ## Frontends
//!
//! The default features do not enable a frontend, so you must choose one of the following:
//!
//! - `std`: enables the std-based API.
//! - `tokio1`: enables the Tokio-based API.
//!
//! Both can exist at the same time, but generally you'll want to use one or the other.
//!
//! ## Wrappers
//!
//! - `creation-flags`: **default**, enables the creation flags wrapper.
//! - `job-object`: **default**, enables the job object wrapper.
//! - `kill-on-drop`: **default**, enables the kill on drop wrapper (Tokio-only).
//! - `process-group`: **default**, enables the process group wrapper.
//! - `process-session`: **default**, enables the process session wrapper.
//!
#![doc(html_favicon_url = "https://watchexec.github.io/logo:command-group.svg")]
#![doc(html_logo_url = "https://watchexec.github.io/logo:command-group.svg")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![warn(missing_docs)]
pub(crate) mod generic_wrap;
#[cfg(feature = "std")]
pub mod std;
#[cfg(feature = "tokio1")]
pub mod tokio;
#[cfg(all(
windows,
feature = "job-object",
any(feature = "std", feature = "tokio1")
))]
mod windows;
/// Internal memoization of the exit status of a child process.
#[allow(dead_code)] // easier than listing exactly which featuresets use it
#[derive(Debug)]
pub(crate) enum ChildExitStatus {
Running,
Exited(::std::process::ExitStatus),
}
#[cfg(all(unix, any(feature = "std", feature = "tokio1")))]
pub use nix::sys::signal::Signal;