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}