### Quick start: process-wrap with std feature Source: https://docs.rs/process-wrap/latest/src/process_wrap/lib.rs.html Quick start example: add the dependency with the "std" feature, then create a CommandWrap, wrap it with ProcessGroup on Unix or JobObject on Windows, spawn, wait, and debug the status. This is equivalent to command-group 5.x usage; for versions 4.x and below, replace ProcessGroup::leader() with ProcessSession. ```toml [dependencies] process-wrap = { version = "10.0.0", features = ["std"] } ``` ```rust # fn main() -> std::io::Result<()> { use process_wrap::std::*; let mut command = CommandWrap::with_new("watch", |command| { command.arg("ls"); }); #[cfg(unix)] { command.wrap(ProcessGroup::leader()); } #[cfg(windows)] { command.wrap(JobObject); } let mut child = command.spawn()?; let status = child.wait()?; dbg!(status); # Ok(()) } ``` -------------------------------- ### Quick start: spawn and wait with process_wrap Source: https://docs.rs/process-wrap/latest/index.html Creates a CommandWrap for 'watch ls', wraps it with ProcessGroup on Unix or JobObject on Windows, then spawns and waits. Uses the std frontend. ```rust use process_wrap::std::*; let mut command = CommandWrap::with_new("watch", |command| { command.arg("ls"); }); #[cfg(unix)] { command.wrap(ProcessGroup::leader()); } #[cfg(windows)] { command.wrap(JobObject); } let mut child = command.spawn()?; let status = child.wait()?; dbg!(status); ``` -------------------------------- ### Wrap a command with log file output Source: https://docs.rs/process-wrap/latest/src/process_wrap/lib.rs.html Demonstrates how to use CommandWrap to execute a command and capture its output into a temporary log file. This example includes the implementation of a custom wrapper to handle the child process lifecycle. ```rust # &mut self, # command: &mut Command, # _child: &mut Child, # _core: &CommandWrap, # ) -> io::Result<()> { # command.stdout(Stdio::null()).stderr(Stdio::null()); # # Ok(()) # } # # fn wrap_child( # &mut self, # child: Box, # _core: &CommandWrap, # ) -> io::Result> { # let wrapped_child = LogFileChild { # inner: child, # thread: mem::take(&mut self.thread), # }; # Ok(Box::new(wrapped_child)) # } # } # # #[derive(Debug)] # struct LogFileChild { # inner: Box, # thread: Option>, # } # # impl ChildWrapper for LogFileChild { # fn inner(&self) -> &dyn ChildWrapper { # &*self.inner # } # # fn inner_mut(&mut self) -> &mut dyn ChildWrapper { # &mut *self.inner # } # # fn into_inner(self: Box) -> Box { # self.inner # } # # #[cfg(windows)] # fn process_handle( # &self, # ) -> Option> { # self.inner.process_handle() # } # # fn wait(&mut self) -> io::Result { # let exit_status = self.inner.wait(); # # if let Some(thread) = mem::take(&mut self.thread) { # thread.join().unwrap(); # } # # exit_status # } # } # fn main() -> Result<(), Box> { #[cfg(windows)] let mut command = CommandWrap::with_new("cmd", |command| { command.args(["/c", "echo Hello && echo World 1>&2"]); }); #[cfg(unix)] let mut command = CommandWrap::with_new("sh", |command| { command.args(["-c", "echo Hello && echo World 1>&2"]); }); let logfile = NamedTempFile::new()?; let logfile_path = logfile.path(); command.wrap(LogFile::new(logfile_path)).spawn()?.wait()?; let logfile_lines: Vec = fs::read_to_string(logfile_path)? .lines() .map(|l| l.trim().into()) .collect(); assert_eq!(logfile_lines, vec!["Hello", "World"]); Ok(()) } ``` -------------------------------- ### Example ChildWrapper implementation Source: https://docs.rs/process-wrap/latest/process_wrap/std/trait.ChildWrapper.html Shows a minimal wrapper around std::process::Child that implements the required methods. Includes a Windows-specific process_handle override. ```rust use process_wrap::std::*; use std::process::Child; #[derive(Debug)] pub struct YourChildWrapper(Child); impl ChildWrapper for YourChildWrapper { fn inner(&self) -> &dyn ChildWrapper { &self.0 } fn inner_mut(&mut self) -> &mut dyn ChildWrapper { &mut self.0 } fn into_inner(self: Box) -> Box { Box::new((*self).0) } #[cfg(windows)] fn process_handle( &self, ) -> Option> { self.0.process_handle() } } ``` -------------------------------- ### Implement ChildWrapper for a custom wrapper Source: https://docs.rs/process-wrap/latest/process_wrap/tokio/trait.ChildWrapper.html Example wrapper implementation for `ChildWrapper`. It wraps a `tokio::process::Child` and implements the three required methods, plus a Windows-only `process_handle` override. The wrapper must be acyclic and terminate in a native `Child` or a self-returning non-native child. ```rust use process_wrap::tokio::*; use tokio::process::Child; #[derive(Debug)] pub struct YourChildWrapper(Child); impl ChildWrapper for YourChildWrapper { fn inner(&self) -> &dyn ChildWrapper { &self.0 } fn inner_mut(&mut self) -> &mut dyn ChildWrapper { &mut self.0 } fn into_inner(self: Box) -> Box { Box::new((*self).0) } #[cfg(windows)] fn process_handle( &self, ) -> Option> { self.0.process_handle() } } ``` -------------------------------- ### Minimal CommandWrapper Implementation Source: https://docs.rs/process-wrap/latest/process_wrap/std/trait.CommandWrapper.html An example of a minimal implementation of the CommandWrapper trait. ```rust #[derive(Debug)] pub struct YourWrapper; impl CommandWrapper for YourWrapper {} ``` -------------------------------- ### Implement LogFile CommandWrapper and ChildWrapper Source: https://docs.rs/process-wrap/latest/src/process_wrap/lib.rs.html Defines the LogFile struct and its implementation of CommandWrapper for pre-spawn setup and ChildWrapper for managing the logging thread and process lifecycle. ```rust fn pre_spawn(&mut self, command: &mut Command, _core: &CommandWrap) -> io::Result<()> { let mut logfile = File::create(&self.path)?; let (mut rx, tx) = io::pipe()?; self.thread = Some(thread::spawn(move || { io::copy(&mut rx, &mut logfile).unwrap(); })); command.stdout(tx.try_clone()?).stderr(tx); Ok(()) } fn wrap_child( &mut self, child: Box, _core: &CommandWrap, ) -> io::Result> { let wrapped_child = LogFileChild { inner: child, thread: mem::take(&mut self.thread), }; Ok(Box::new(wrapped_child)) } } #[derive(Debug)] struct LogFileChild { inner: Box, thread: Option>, } impl ChildWrapper for LogFileChild { fn inner(&self) -> &dyn ChildWrapper { &*self.inner } fn inner_mut(&mut self) -> &mut dyn ChildWrapper { &mut *self.inner } fn into_inner(self: Box) -> Box { self.inner } #[cfg(windows)] fn process_handle( &self, ) -> Option> { self.inner.process_handle() } fn wait(&mut self) -> io::Result { let exit_status = self.inner.wait(); if let Some(thread) = mem::take(&mut self.thread) { thread.join().unwrap(); } exit_status } } ``` -------------------------------- ### ProcessGroupChild::pgid Source: https://docs.rs/process-wrap/latest/process_wrap/std/struct.ProcessGroupChild.html Get the process group ID of this child process. ```APIDOC ## ProcessGroupChild::pgid ### Description Get the process group ID of this child process. ### Method `pub fn pgid(&self) -> u32` ### Parameters None. ### Returns - `u32` - The process group ID of the child process. ``` -------------------------------- ### Apply Windows Creation Flags Source: https://docs.rs/process-wrap/latest/index.html Demonstrates how to apply Windows-specific creation flags using CommandWrap. ```rust use process_wrap::std::*; let mut command = CommandWrap::with_new("ls", |command| { command.arg("-l"); }); command.wrap(CreationFlags(CREATE_NO_WINDOW)); ``` -------------------------------- ### CommandWrap::get_wrap Source: https://docs.rs/process-wrap/latest/process_wrap/std/struct.CommandWrap.html Gets a reference to a wrapper of a given type. Useful for accessing wrapper state from within another wrapper. ```APIDOC ## CommandWrap::get_wrap ### Description Gets a reference to a wrapper of a given type. Useful for accessing wrapper state from within another wrapper. ### Method `pub fn get_wrap(&self) -> Option<&W>` ### Returns - **Option<&W>** - A reference to the wrapper if present, `None` otherwise. ### Notes While a wrapper's lifecycle hook is running, that active wrapper is temporarily unavailable through this method; peer wrappers remain available. Use `has_wrap` to merely check registration. ``` -------------------------------- ### Spawn a command with process-wrap Source: https://docs.rs/process-wrap Demonstrates spawning a command using CommandWrap, applying platform-specific wrappers for process groups on Unix or job objects on Windows. ```rust use process_wrap::std::*; let mut command = CommandWrap::with_new("watch", |command| { command.arg("ls"); }); #[cfg(unix)] { command.wrap(ProcessGroup::leader()); } #[cfg(windows)] { command.wrap(JobObject); } let mut child = command.spawn()?; let status = child.wait()?; dbg!(status); ``` -------------------------------- ### Implement a Basic Tokio Wrapper Source: https://docs.rs/process-wrap/latest/index.html Shows the minimal implementation required to create a custom wrapper for Tokio. ```rust use process_wrap::tokio::*; #[derive(Debug)] pub struct YourWrapper; impl CommandWrapper for YourWrapper {} ``` -------------------------------- ### Construct CommandWrap with a closure Source: https://docs.rs/process-wrap/latest/index.html Shows a more ergonomic way to create a CommandWrap using with_new and a closure to configure the command, then add platform-specific wrappers. ```rust use process_wrap::std::*; let mut command = CommandWrap::with_new("ls", |command| { command.arg("-l"); }); #[cfg(unix)] { command.wrap(ProcessGroup::leader()); } #[cfg(windows)] { command.wrap(JobObject); } ``` -------------------------------- ### CommandWrap::spawn_with Source: https://docs.rs/process-wrap/latest/src/process_wrap/generic_wrap.rs.html Spawns the command using a custom native-child spawner function, running pre_spawn hooks, then the closure, then post_spawn hooks, then wrap_child hooks. ```APIDOC ## CommandWrap::spawn_with ### Description Spawns the command using a custom native-child spawner closure. The closure receives a mutable reference to the underlying command and must return a native Child. Lifecycle: pre_spawn, closure, post_spawn, wrap_child. ### Method spawn_with ### Signature pub fn spawn_with( &mut self, spawner: impl FnOnce(&mut $command) -> ::std::io::Result<$child> ) -> ::std::io::Result> ### Parameters - **spawner** (FnOnce(&mut $command) -> ::std::io::Result<$child>) - Required - Closure that creates the native child process. ### Return Value - **Result>** - Ok on success, Err on IO failure. ``` -------------------------------- ### Construct CommandWrap from an existing process::Command Source: https://docs.rs/process-wrap/latest/index.html Shows how to wrap an existing std::process::Command with CommandWrap and add platform-specific wrappers (ProcessGroup on Unix, JobObject on Windows). ```rust use process_wrap::std::*; use std::process::Command; let mut command = Command::new("ls"); command.arg("-l"); let mut command = CommandWrap::from(command); #[cfg(unix)] { command.wrap(ProcessGroup::leader()); } #[cfg(windows)] { command.wrap(JobObject); } ``` -------------------------------- ### CommandWrap::spawn Source: https://docs.rs/process-wrap/latest/process_wrap/tokio/struct.CommandWrap.html Spawns the command and returns a boxed child wrapper. ```APIDOC ## CommandWrap::spawn ### Description Spawns the command, returning a boxed `ChildWrapper`. Runs all `pre_spawn` hooks, spawns the command, runs all `post_spawn` hooks, and stacks all `wrap_child`s. ### Method `spawn` ### Signature ```rust pub fn spawn(&mut self) -> Result> ``` ### Returns - `Result>` - A boxed trait object representing the spawned child process. Downcast to the concrete type of the last applied wrapper if needed. ``` -------------------------------- ### CommandWrap::spawn Source: https://docs.rs/process-wrap/latest/index.html Spawns the process and returns a Child that can be interacted with. Methods on Child mimic those on process::Child but may be customized by the wrappers. ```APIDOC ## CommandWrap::spawn ### Description Spawns the process and returns a Child that can be interacted with. Methods on Child mimic those on process::Child but may be customized by the wrappers. ### Method spawn ### Returns - **Child** - The spawned child process. ### Example ```rust let mut child = command.spawn(); ``` ``` -------------------------------- ### Test LogFile wrapper with CommandWrap Source: https://docs.rs/process-wrap/latest/index.html Tests the LogFile wrapper by running a command that echoes to stdout and stderr, then verifies the log file contains both lines. Uses cfg to select cmd on Windows and sh on Unix. ```rust fn main() -> Result<(), Box> { #[cfg(windows)] let mut command = CommandWrap::with_new("cmd", |command| { command.args(["/c", "echo Hello && echo World 1>&2"]); }); #[cfg(unix)] let mut command = CommandWrap::with_new("sh", |command| { command.args(["-c", "echo Hello && echo World 1>&2"]); }); let logfile = NamedTempFile::new()?; let logfile_path = logfile.path(); command.wrap(LogFile::new(logfile_path)).spawn()?.wait()?; let logfile_lines: Vec = fs::read_to_string(logfile_path)? .lines() .map(|l| l.trim().into()) .collect(); assert_eq!(logfile_lines, vec!["Hello", "World"]); Ok(()) } ``` -------------------------------- ### CommandWrap::spawn Source: https://docs.rs/process-wrap/latest/process_wrap/std/struct.CommandWrap.html Spawns the command, returning a boxed ChildWrapper. Runs pre_spawn hooks, spawns the command, runs post_spawn hooks, and stacks wrap_child hooks. ```APIDOC ## CommandWrap::spawn ### Description Spawns the command, returning a boxed `ChildWrapper`. Runs all `pre_spawn` hooks, then spawns the command, then runs all `post_spawn` hooks, then stacks all `wrap_child`s. ### Method `pub fn spawn(&mut self) -> Result>` ### Returns - **Result>** - A boxed trait object representing the spawned child process. ### Notes Only methods from the trait are available directly; you may downcast to the concrete type of the last applied wrapper if needed. ``` -------------------------------- ### CommandWrap::spawn Source: https://docs.rs/process-wrap/latest/process_wrap Spawns the process, returning a Child that can be interacted with. ```APIDOC ## CommandWrap::spawn ### Description Spawns 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. ### Method spawn ### Returns - **Child** - The spawned child process wrapper. ### Example ```rust let child = command.spawn(); ``` ``` -------------------------------- ### CommandWrap::with_new Source: https://docs.rs/process-wrap/latest/process_wrap/std/struct.CommandWrap.html Creates a new CommandWrap from a program name and a closure to configure the command. This is a convenience method that creates a new Command, calls the closure, and wraps it. ```APIDOC ## CommandWrap::with_new ### Description Creates a new `CommandWrap` from a program name and a closure to configure the command. This is a convenience method that creates a new `Command`, calls the closure, and wraps it. ### Method `pub fn with_new(program: impl AsRef, init: impl FnOnce(&mut Command)) -> Self` ### Parameters - **program** (impl AsRef) - Required - The program name to run. - **init** (impl FnOnce(&mut Command)) - Required - A closure to configure the command. ### Returns - **Self** - A new `CommandWrap` instance. ### Notes Alternatively, use `From`/`Into` to convert a `Command` to a `CommandWrap`. ``` -------------------------------- ### CommandWrap::spawn_with Source: https://docs.rs/process-wrap/latest/process_wrap/std/struct.CommandWrap.html Spawns the command using a custom native-child spawner function. The lifecycle is the same as spawn, but the provided closure creates the native child process. ```APIDOC ## CommandWrap::spawn_with ### Description Spawns the command using a custom native-child spawner function. The lifecycle is the same as `spawn`, but the provided closure creates the native child process. ### Method `pub fn spawn_with(&mut self, spawner: impl FnOnce(&mut Command) -> Result) -> Result>` ### Parameters - **spawner** (impl FnOnce(&mut Command) -> Result) - Required - A closure that creates the native child process. ### Returns - **Result>** - A boxed trait object representing the spawned child process. ### Notes All `pre_spawn` hooks run first, then the closure is called, then `post_spawn` hooks, then `wrap_child`. ``` -------------------------------- ### Importing tokio module Source: https://docs.rs/process-wrap/latest/process_wrap/tokio/index.html The recommended way to use the tokio module is to star-import it. ```rust use process_wrap::tokio::*; ``` -------------------------------- ### Use KillOnDrop shim with CommandWrap (Tokio) Source: https://docs.rs/process-wrap/latest/index.html Shows how to use the KillOnDrop shim instead of .kill_on_drop(true) on Command, which is required for JobObject on Windows to access the setting. ```rust use process_wrap::tokio::*; let mut command = CommandWrap::with_new("ls", |command| { command.arg("-l"); }); command.wrap(KillOnDrop); ``` -------------------------------- ### CommandWrap::with_new Source: https://docs.rs/process-wrap/latest/process_wrap Constructs a CommandWrap using a closure to configure a new command. ```APIDOC ## CommandWrap::with_new ### Description Constructs a CommandWrap using a closure to configure a new command. ### Method with_new ### Parameters - **program** (string) - Required - The program to run. - **configure** (closure) - Required - A closure that receives a mutable reference to the Command for configuration. ### Example ```rust use process_wrap::std::*; let mut command = CommandWrap::with_new("ls", |command| { command.arg("-l"); }); #[cfg(unix)] { command.wrap(ProcessGroup::leader()); } #[cfg(windows)] { command.wrap(JobObject); } ``` ``` -------------------------------- ### Constructing CommandWrap: from, with_new, and fluent style Source: https://docs.rs/process-wrap/latest/src/process_wrap/lib.rs.html Three ways to construct a CommandWrap: from an existing std::process::Command, via the closure pattern with CommandWrap::with_new, and a fluent style for single-platform targets. The wrap method can be called multiple times; wrappers are applied in the order added. ```rust use process_wrap::std::*; use std::process::Command; let mut command = Command::new("ls"); command.arg("-l"); let mut command = CommandWrap::from(command); #[cfg(unix)] { command.wrap(ProcessGroup::leader()); } #[cfg(windows)] { command.wrap(JobObject); } ``` ```rust use process_wrap::std::*; let mut command = CommandWrap::with_new("ls", |command| { command.arg("-l"); }); #[cfg(unix)] { command.wrap(ProcessGroup::leader()); } #[cfg(windows)] { command.wrap(JobObject); } ``` ```rust use process_wrap::std::*; CommandWrap::with_new("ls", |command| { command.arg("-l"); }) .wrap(ProcessGroup::leader()); ``` -------------------------------- ### Implement a Logging Wrapper Source: https://docs.rs/process-wrap/latest/index.html An initial implementation of a logging wrapper that redirects stdout and stderr to a file using a background thread. ```rust #[derive(Debug)] struct LogFile { path: PathBuf, } impl LogFile { fn new(path: impl Into) -> Self { Self { path: path.into() } } } impl CommandWrapper for LogFile { fn pre_spawn(&mut self, command: &mut Command, _core: &CommandWrap) -> io::Result<()> { let mut logfile = File::create(&self.path)?; let (mut rx, tx) = io::pipe()?; thread::spawn(move || { io::copy(&mut rx, &mut logfile).unwrap(); }); command.stdout(tx.try_clone()?).stderr(tx); Ok(()) } } ``` -------------------------------- ### Fluent style for single-platform wrapping Source: https://docs.rs/process-wrap/latest/index.html Shows a fluent style for wrapping a command when targeting a single platform, using ProcessGroup::leader() on Unix. ```rust use process_wrap::std::*; CommandWrap::with_new("ls", |command| { command.arg("-l"); }) .wrap(ProcessGroup::leader()); ``` -------------------------------- ### spawn public API Source: https://docs.rs/process-wrap/latest/src/process_wrap/generic_wrap.rs.html Spawns the command by running pre-spawn hooks, spawning, post-spawn hooks, and wrapping the child. ```rust pub fn spawn(&mut self) -> ::std::io::Result> { self.spawn_with(|command| command.spawn()) } ``` -------------------------------- ### CommandWrap::from Source: https://docs.rs/process-wrap/latest/index.html Constructs a CommandWrap from an existing std::process::Command. This allows wrapping an already configured command with additional process wrappers. ```APIDOC ## CommandWrap::from ### Description Constructs a CommandWrap from an existing std::process::Command. This allows wrapping an already configured command with additional process wrappers. ### Method from ### Parameters - **command** (std::process::Command) - Required - The command to wrap. ### Example ```rust use process_wrap::std::*; use std::process::Command; let mut command = Command::new("ls"); command.arg("-l"); let mut command = CommandWrap::from(command); #[cfg(unix)] { command.wrap(ProcessGroup::leader()); } #[cfg(windows)] { command.wrap(JobObject); } ``` ``` -------------------------------- ### Importing std module Source: https://docs.rs/process-wrap/latest/process_wrap/std/index.html The recommended way to use the std-based process-wrap API is to perform a star import from the module. ```rust use process_wrap::std::*; ``` -------------------------------- ### CommandWrap::command Source: https://docs.rs/process-wrap/latest/process_wrap/std/struct.CommandWrap.html Returns a reference to the wrapped Command. ```APIDOC ## CommandWrap::command ### Description Returns a reference to the wrapped `Command`. ### Method `pub fn command(&self) -> &Command` ### Returns - **&Command** - A reference to the wrapped command. ``` -------------------------------- ### CommandWrap::into_command Source: https://docs.rs/process-wrap/latest/process_wrap/std/struct.CommandWrap.html Consumes the CommandWrap and returns the wrapped Command. ```APIDOC ## CommandWrap::into_command ### Description Consumes the `CommandWrap` and returns the wrapped `Command`. ### Method `pub fn into_command(self) -> Command` ### Returns - **Command** - The wrapped command. ``` -------------------------------- ### Test LogFile CommandWrapper Source: https://docs.rs/process-wrap/latest/src/process_wrap/lib.rs.html Provides a skeleton for testing the LogFile implementation. ```rust # use process_wrap::std::{ChildWrapper, CommandWrap, CommandWrapper}; # use std::{ # error::Error, # fs::{self, File}, # io, mem, # path::PathBuf, # process::{Child, Command, ExitStatus, Stdio}, # thread::{self, JoinHandle}, # }; # use tempfile::NamedTempFile; # #[derive(Debug)] # struct LogFile { # path: PathBuf, # thread: Option>, # } # # impl LogFile { # fn new(path: impl Into) -> Self { # Self { # path: path.into(), # thread: None, # } # } # } # # impl CommandWrapper for LogFile { # fn pre_spawn(&mut self, command: &mut Command, _core: &CommandWrap) -> io::Result<()> { # let mut logfile = File::create(&self.path)?; # let (mut rx, tx) = io::pipe()?; # # self.thread = Some(thread::spawn(move || { # io::copy(&mut rx, &mut logfile).unwrap(); # })); # # command.stdout(tx.try_clone()?).stderr(tx); # Ok(()) # } # # fn post_spawn( ```