Auto merge of #42133 - cuviper:stdio-from, r=alexcrichton

Add conversions from File and Child* handles to Stdio

`Stdio` now implements `From<ChildStdin>`, `From<ChildStdout>`,
`From<ChildStderr>`, and `From<File>`.

The `Command::stdin`/`stdout`/`stderr` methods now take any type that
implements `Into<Stdio>`.

This makes it much easier to write shell-like command chains, piping to
one another and redirecting to and from files.  Otherwise one would need
to use the unsafe and OS-specific `from_raw_fd` or `from_raw_handle`.
This commit is contained in:
bors 2017-06-07 02:11:20 +00:00
commit 21d0f91444
6 changed files with 156 additions and 8 deletions

View File

@ -59,6 +59,7 @@ use io::prelude::*;
use ffi::OsStr;
use fmt;
use fs;
use io;
use path::Path;
use str;
@ -544,8 +545,8 @@ impl Command {
/// .expect("ls command failed to start");
/// ```
#[stable(feature = "process", since = "1.0.0")]
pub fn stdin(&mut self, cfg: Stdio) -> &mut Command {
self.inner.stdin(cfg.0);
pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
self.inner.stdin(cfg.into().0);
self
}
@ -564,8 +565,8 @@ impl Command {
/// .expect("ls command failed to start");
/// ```
#[stable(feature = "process", since = "1.0.0")]
pub fn stdout(&mut self, cfg: Stdio) -> &mut Command {
self.inner.stdout(cfg.0);
pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
self.inner.stdout(cfg.into().0);
self
}
@ -584,8 +585,8 @@ impl Command {
/// .expect("ls command failed to start");
/// ```
#[stable(feature = "process", since = "1.0.0")]
pub fn stderr(&mut self, cfg: Stdio) -> &mut Command {
self.inner.stderr(cfg.0);
pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
self.inner.stderr(cfg.into().0);
self
}
@ -753,6 +754,34 @@ impl fmt::Debug for Stdio {
}
}
#[stable(feature = "stdio_from", since = "1.20.0")]
impl From<ChildStdin> for Stdio {
fn from(child: ChildStdin) -> Stdio {
Stdio::from_inner(child.into_inner().into())
}
}
#[stable(feature = "stdio_from", since = "1.20.0")]
impl From<ChildStdout> for Stdio {
fn from(child: ChildStdout) -> Stdio {
Stdio::from_inner(child.into_inner().into())
}
}
#[stable(feature = "stdio_from", since = "1.20.0")]
impl From<ChildStderr> for Stdio {
fn from(child: ChildStderr) -> Stdio {
Stdio::from_inner(child.into_inner().into())
}
}
#[stable(feature = "stdio_from", since = "1.20.0")]
impl From<fs::File> for Stdio {
fn from(file: fs::File) -> Stdio {
Stdio::from_inner(file.into_inner().into())
}
}
/// Describes the result of a process after it has terminated.
///
/// This `struct` is used to represent the exit status of a child process.

View File

@ -400,6 +400,18 @@ impl Stdio {
}
}
impl From<AnonPipe> for Stdio {
fn from(pipe: AnonPipe) -> Stdio {
Stdio::Fd(pipe.into_fd())
}
}
impl From<File> for Stdio {
fn from(file: File) -> Stdio {
Stdio::Fd(file.into_fd())
}
}
impl ChildStdio {
fn fd(&self) -> Option<usize> {
match *self {

View File

@ -315,6 +315,18 @@ impl Stdio {
}
}
impl From<AnonPipe> for Stdio {
fn from(pipe: AnonPipe) -> Stdio {
Stdio::Fd(pipe.into_fd())
}
}
impl From<File> for Stdio {
fn from(file: File) -> Stdio {
Stdio::Fd(file.into_fd())
}
}
impl ChildStdio {
pub fn fd(&self) -> Option<c_int> {
match *self {

View File

@ -306,6 +306,18 @@ impl Stdio {
}
}
impl From<AnonPipe> for Stdio {
fn from(pipe: AnonPipe) -> Stdio {
Stdio::Handle(pipe.into_handle())
}
}
impl From<File> for Stdio {
fn from(file: File) -> Stdio {
Stdio::Handle(file.into_handle())
}
}
////////////////////////////////////////////////////////////////////////////////
// Processes
////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,83 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-cross-compile
#![feature(rustc_private)]
extern crate rustc_back;
use std::env;
use std::fs::File;
use std::io;
use std::io::{Read, Write};
use std::process::{Command, Stdio};
use rustc_back::tempdir::TempDir;
fn main() {
if env::args().len() > 1 {
child().unwrap()
} else {
parent().unwrap()
}
}
fn parent() -> io::Result<()> {
let td = TempDir::new("foo").unwrap();
let input = td.path().join("input");
let output = td.path().join("output");
File::create(&input)?.write_all(b"foo\n")?;
// Set up this chain:
// $ me <file | me | me >file
// ... to duplicate each line 8 times total.
let mut child1 = Command::new(env::current_exe()?)
.arg("first")
.stdin(File::open(&input)?) // tests File::into()
.stdout(Stdio::piped())
.spawn()?;
let mut child3 = Command::new(env::current_exe()?)
.arg("third")
.stdin(Stdio::piped())
.stdout(File::create(&output)?) // tests File::into()
.spawn()?;
// Started out of order so we can test both `ChildStdin` and `ChildStdout`.
let mut child2 = Command::new(env::current_exe()?)
.arg("second")
.stdin(child1.stdout.take().unwrap()) // tests ChildStdout::into()
.stdout(child3.stdin.take().unwrap()) // tests ChildStdin::into()
.spawn()?;
assert!(child1.wait()?.success());
assert!(child2.wait()?.success());
assert!(child3.wait()?.success());
let mut data = String::new();
File::open(&output)?.read_to_string(&mut data)?;
for line in data.lines() {
assert_eq!(line, "foo");
}
assert_eq!(data.lines().count(), 8);
Ok(())
}
fn child() -> io::Result<()> {
// double everything
let mut input = vec![];
io::stdin().read_to_end(&mut input)?;
io::stdout().write_all(&input)?;
io::stdout().write_all(&input)?;
Ok(())
}

View File

@ -69,8 +69,8 @@ fn main() {
Command::new(name)
.arg("--child")
.stdin(Stdio::inherit())
.stdout(unsafe { FromRawFd::from_raw_fd(libc::STDERR_FILENO) })
.stderr(unsafe { FromRawFd::from_raw_fd(libc::STDOUT_FILENO) })
.stdout(unsafe { Stdio::from_raw_fd(libc::STDERR_FILENO) })
.stderr(unsafe { Stdio::from_raw_fd(libc::STDOUT_FILENO) })
.spawn()
};