process: allow for ergonomically piping stdio of one child into another (#3466)

* Add `TryInto<Stdio>` impls for `ChildStd{in,out,err}` for ergonomic
  conversions into `std::process::Stdio` so callers can perform the
  conversion without needing to manipulate raw fds/handles directly
This commit is contained in:
Ivan Petkov
2021-01-27 01:12:54 +00:00
committed by GitHub
parent f13a9dd87a
commit 34f1d3d040
5 changed files with 195 additions and 19 deletions
+70
View File
@@ -97,6 +97,51 @@
//! }
//! ```
//!
//! With some coordination, we can also pipe the output of one command into
//! another.
//!
//! ```no_run
//! use tokio::join;
//! use tokio::process::Command;
//! use std::convert::TryInto;
//! use std::process::Stdio;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut echo = Command::new("echo")
//! .arg("hello world!")
//! .stdout(Stdio::piped())
//! .spawn()
//! .expect("failed to spawn echo");
//!
//! let tr_stdin: Stdio = echo
//! .stdout
//! .take()
//! .unwrap()
//! .try_into()
//! .expect("failed to convert to Stdio");
//!
//! let tr = Command::new("tr")
//! .arg("a-z")
//! .arg("A-Z")
//! .stdin(tr_stdin)
//! .stdout(Stdio::piped())
//! .spawn()
//! .expect("failed to spawn tr");
//!
//! let (echo_result, tr_output) = join!(echo.wait(), tr.wait_with_output());
//!
//! assert!(echo_result.unwrap().success());
//!
//! let tr_output = tr_output.expect("failed to await tr");
//! assert!(tr_output.status.success());
//!
//! assert_eq!(tr_output.stdout, b"HELLO WORLD!\n");
//!
//! Ok(())
//! }
//! ```
//!
//! # Caveats
//!
//! ## Dropping/Cancellation
@@ -147,6 +192,7 @@ mod kill;
use crate::io::{AsyncRead, AsyncWrite, ReadBuf};
use crate::process::kill::Kill;
use std::convert::TryInto;
use std::ffi::OsStr;
use std::future::Future;
use std::io;
@@ -1099,6 +1145,30 @@ impl AsyncRead for ChildStderr {
}
}
impl TryInto<Stdio> for ChildStdin {
type Error = io::Error;
fn try_into(self) -> Result<Stdio, Self::Error> {
imp::convert_to_stdio(self.inner)
}
}
impl TryInto<Stdio> for ChildStdout {
type Error = io::Error;
fn try_into(self) -> Result<Stdio, Self::Error> {
imp::convert_to_stdio(self.inner)
}
}
impl TryInto<Stdio> for ChildStderr {
type Error = io::Error;
fn try_into(self) -> Result<Stdio, Self::Error> {
imp::convert_to_stdio(self.inner)
}
}
#[cfg(unix)]
mod sys {
use std::os::unix::io::{AsRawFd, RawFd};