From 9debe91675222782e08fbb15bb6359a05bf85131 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 6 Jun 2017 15:42:55 -0700 Subject: [PATCH] Add conversions from File and Child* handles to Stdio `Stdio` now implements `From`, `From`, `From`, and `From`. The `Command::stdin`/`stdout`/`stderr` methods now take any type that implements `Into`. 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`. --- src/libstd/process.rs | 41 +++++++-- src/libstd/sys/redox/process.rs | 12 +++ src/libstd/sys/unix/process/process_common.rs | 12 +++ src/libstd/sys/windows/process.rs | 12 +++ src/test/run-pass-fulldeps/stdio-from.rs | 83 +++++++++++++++++++ src/test/run-pass/issue-30490.rs | 4 +- 6 files changed, 156 insertions(+), 8 deletions(-) create mode 100644 src/test/run-pass-fulldeps/stdio-from.rs diff --git a/src/libstd/process.rs b/src/libstd/process.rs index da64704efba..4c6d88c0ae8 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -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>(&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>(&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>(&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 for Stdio { + fn from(child: ChildStdin) -> Stdio { + Stdio::from_inner(child.into_inner().into()) + } +} + +#[stable(feature = "stdio_from", since = "1.20.0")] +impl From for Stdio { + fn from(child: ChildStdout) -> Stdio { + Stdio::from_inner(child.into_inner().into()) + } +} + +#[stable(feature = "stdio_from", since = "1.20.0")] +impl From for Stdio { + fn from(child: ChildStderr) -> Stdio { + Stdio::from_inner(child.into_inner().into()) + } +} + +#[stable(feature = "stdio_from", since = "1.20.0")] +impl From 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. diff --git a/src/libstd/sys/redox/process.rs b/src/libstd/sys/redox/process.rs index 95e9438cd71..62d873d257d 100644 --- a/src/libstd/sys/redox/process.rs +++ b/src/libstd/sys/redox/process.rs @@ -400,6 +400,18 @@ impl Stdio { } } +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + Stdio::Fd(pipe.into_fd()) + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + Stdio::Fd(file.into_fd()) + } +} + impl ChildStdio { fn fd(&self) -> Option { match *self { diff --git a/src/libstd/sys/unix/process/process_common.rs b/src/libstd/sys/unix/process/process_common.rs index e9f41009064..32fcee1e461 100644 --- a/src/libstd/sys/unix/process/process_common.rs +++ b/src/libstd/sys/unix/process/process_common.rs @@ -315,6 +315,18 @@ impl Stdio { } } +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + Stdio::Fd(pipe.into_fd()) + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + Stdio::Fd(file.into_fd()) + } +} + impl ChildStdio { pub fn fd(&self) -> Option { match *self { diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index 0bd0ce73138..0d1766d5aec 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -306,6 +306,18 @@ impl Stdio { } } +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + Stdio::Handle(pipe.into_handle()) + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + Stdio::Handle(file.into_handle()) + } +} + //////////////////////////////////////////////////////////////////////////////// // Processes //////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/run-pass-fulldeps/stdio-from.rs b/src/test/run-pass-fulldeps/stdio-from.rs new file mode 100644 index 00000000000..f64bbf9312c --- /dev/null +++ b/src/test/run-pass-fulldeps/stdio-from.rs @@ -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 or the MIT license +// , 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 + // ... 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(()) +} diff --git a/src/test/run-pass/issue-30490.rs b/src/test/run-pass/issue-30490.rs index 035911302cf..7658abc00c5 100644 --- a/src/test/run-pass/issue-30490.rs +++ b/src/test/run-pass/issue-30490.rs @@ -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() };