pwner/
lib.rs

1#![deny(warnings, missing_docs, clippy::pedantic, clippy::all)]
2#![warn(rust_2018_idioms)]
3#![forbid(unsafe_code)]
4
5//! Pwner is a Process Owner crate that allows ergonomic access to child processes.
6//!
7//! This module creates the possibility of owning a child and having convenient methods to
8//! read and write, while also killing the process gracefully upon dropping.
9//!
10//! # Spawning an owned process
11//!
12//! ```no_run
13//! use std::process::Command;
14//! use pwner::Spawner;
15//!
16//! Command::new("ls").spawn_owned().expect("ls command failed to start");
17//! ```
18//!
19//! # Reading and writing
20//!
21//! ```no_run
22//! use std::io::{BufRead, BufReader, Write};
23//! use std::process::Command;
24//! use pwner::Spawner;
25//!
26//! # fn wrapper() -> std::io::Result<()> {
27//! let mut child = Command::new("cat").spawn_owned()?;
28//! child.write_all(b"hello\n")?;
29//!
30//! let mut output = String::new();
31//! let mut reader = BufReader::new(child);
32//! reader.read_line(&mut output)?;
33//!
34//! assert_eq!("hello\n", output);
35//! # Ok(())
36//! # }
37//! ```
38//!
39//! # Stopping an owned process
40//!
41//! The owned process is terminated whenever it is dropped.
42//!
43//! ## Example
44//!
45//! ```no_run
46//! use std::process::Command;
47//! use pwner::Spawner;
48//!
49//! {
50//!     let child = Command::new("ls").spawn_owned().expect("ls command failed to start");
51//! }
52//! // child is killed when dropped out of scope
53//! ```
54//!
55//! # Graceful dropping
56//!
57//! **Note:** Only available on *nix platforms.
58//!
59//! When the owned process gets dropped, [`Process`] will try to kill it gracefully by sending a
60//! `SIGINT`. If the process still doesn't die, a `SIGTERM` is sent and another chance is given,
61//! until finally a `SIGKILL` is sent.
62pub mod process;
63#[cfg(feature = "tokio")]
64pub mod tokio;
65
66/// A process builder, providing the wrapped handle, as well as piped handles to stdin,
67/// stdout, and stderr.
68///
69/// The handle also implements a clean shutdown of the process upon destruction.
70pub trait Spawner {
71    /// The [`Process`] implementation output by [`Self::spawn_owned()`]
72    type Output: Process;
73
74    /// Executes the command as a child process, returning a handle to it.
75    ///
76    /// Upon creation, stid, stdout, and stderr are piped and kept by the handle.
77    ///
78    /// # Examples
79    ///
80    /// Basic usage:
81    ///
82    /// ```no_run
83    /// use std::process::Command;
84    /// use pwner::Spawner;
85    ///
86    /// Command::new("ls")
87    ///         .spawn_owned()
88    ///         .expect("ls command failed to start");
89    /// ```
90    ///
91    /// # Errors
92    ///
93    /// * [`std::io::Error`] if failure when spawning
94    ///
95    /// [`std::io::Error`]: std::io::Error
96    fn spawn_owned(&mut self) -> std::io::Result<Self::Output>;
97}
98
99/// The trait returned by [`Spawner::spawn_owned()`].
100///
101/// All implementations of [`Spawner`] must return a concrete instance capable of read/write.
102pub trait Process {}
103
104#[cfg(unix)]
105#[derive(Debug)]
106enum UnixIoError {
107    Io(std::io::Error),
108    Unix(nix::Error),
109}
110
111#[cfg(unix)]
112impl std::error::Error for UnixIoError {}
113
114#[cfg(unix)]
115impl std::fmt::Display for UnixIoError {
116    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        match self {
118            UnixIoError::Io(e) => e.fmt(fmt),
119            UnixIoError::Unix(e) => e.fmt(fmt),
120        }
121    }
122}
123
124#[cfg(unix)]
125impl std::convert::From<std::io::Error> for UnixIoError {
126    fn from(error: std::io::Error) -> Self {
127        UnixIoError::Io(error)
128    }
129}
130
131#[cfg(unix)]
132impl std::convert::From<nix::Error> for UnixIoError {
133    fn from(error: nix::Error) -> Self {
134        UnixIoError::Unix(error)
135    }
136}