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
#![deny(warnings, missing_docs, clippy::pedantic, clippy::all)]
#![warn(rust_2018_idioms)]
#![forbid(unsafe_code)]

//! Pwner is a Process Owner crate that allows ergonomic access to child processes.
//!
//! This module creates the possibility of owning a child and having convenient methods to
//! read and write, while also killing the process gracefully upon dropping.
//!
//! # Spawning an owned process
//!
//! ```no_run
//! use std::process::Command;
//! use pwner::Spawner;
//!
//! Command::new("ls").spawn_owned().expect("ls command failed to start");
//! ```
//!
//! # Reading and writing
//!
//! ```no_run
//! use std::io::{BufRead, BufReader, Write};
//! use std::process::Command;
//! use pwner::Spawner;
//!
//! # fn wrapper() -> std::io::Result<()> {
//! let mut child = Command::new("cat").spawn_owned()?;
//! child.write_all(b"hello\n")?;
//!
//! let mut output = String::new();
//! let mut reader = BufReader::new(child);
//! reader.read_line(&mut output)?;
//!
//! assert_eq!("hello\n", output);
//! # Ok(())
//! # }
//! ```
//!
//! # Stopping an owned process
//!
//! The owned process is terminated whenever it is dropped.
//!
//! ## Example
//!
//! ```no_run
//! use std::process::Command;
//! use pwner::Spawner;
//!
//! {
//!     let child = Command::new("ls").spawn_owned().expect("ls command failed to start");
//! }
//! // child is killed when dropped out of scope
//! ```
//!
//! # Graceful dropping
//!
//! **Note:** Only available on *nix platforms.
//!
//! When the owned process gets dropped, [`Process`] will try to kill it gracefully by sending a
//! `SIGINT`. If the process still doesn't die, a `SIGTERM` is sent and another chance is given,
//! until finally a `SIGKILL` is sent.
pub mod process;
#[cfg(feature = "tokio")]
pub mod tokio;

/// A process builder, providing the wrapped handle, as well as piped handles to stdin,
/// stdout, and stderr.
///
/// The handle also implements a clean shutdown of the process upon destruction.
pub trait Spawner {
    /// The [`Process`] implementation output by [`Self::spawn_owned()`]
    type Output: Process;

    /// Executes the command as a child process, returning a handle to it.
    ///
    /// Upon creation, stid, stdout, and stderr are piped and kept by the handle.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```no_run
    /// use std::process::Command;
    /// use pwner::Spawner;
    ///
    /// Command::new("ls")
    ///         .spawn_owned()
    ///         .expect("ls command failed to start");
    /// ```
    ///
    /// # Errors
    ///
    /// * [`std::io::Error`] if failure when spawning
    ///
    /// [`std::io::Error`]: std::io::Error
    fn spawn_owned(&mut self) -> std::io::Result<Self::Output>;
}

/// The trait returned by [`Spawner::spawn_owned()`].
///
/// All implementations of [`Spawner`] must return a concrete instance capable of read/write.
pub trait Process {}

#[cfg(unix)]
#[derive(Debug)]
enum UnixIoError {
    Io(std::io::Error),
    Unix(nix::Error),
}

#[cfg(unix)]
impl std::error::Error for UnixIoError {}

#[cfg(unix)]
impl std::fmt::Display for UnixIoError {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            UnixIoError::Io(e) => e.fmt(fmt),
            UnixIoError::Unix(e) => e.fmt(fmt),
        }
    }
}

#[cfg(unix)]
impl std::convert::From<std::io::Error> for UnixIoError {
    fn from(error: std::io::Error) -> Self {
        UnixIoError::Io(error)
    }
}

#[cfg(unix)]
impl std::convert::From<nix::Error> for UnixIoError {
    fn from(error: nix::Error) -> Self {
        UnixIoError::Unix(error)
    }
}