about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJosh Stone <jistone@redhat.com>2017-06-06 15:42:55 -0700
committerJosh Stone <jistone@redhat.com>2017-06-06 15:42:55 -0700
commit9debe91675222782e08fbb15bb6359a05bf85131 (patch)
tree3bc0a130617f2cc0675f47b1647da77e00738412
parenta032cb89c5d9b436c1c57f8a6d5961d898f5c2b6 (diff)
downloadrust-9debe91675222782e08fbb15bb6359a05bf85131.tar.gz
rust-9debe91675222782e08fbb15bb6359a05bf85131.zip
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`.
-rw-r--r--src/libstd/process.rs41
-rw-r--r--src/libstd/sys/redox/process.rs12
-rw-r--r--src/libstd/sys/unix/process/process_common.rs12
-rw-r--r--src/libstd/sys/windows/process.rs12
-rw-r--r--src/test/run-pass-fulldeps/stdio-from.rs83
-rw-r--r--src/test/run-pass/issue-30490.rs4
6 files changed, 156 insertions, 8 deletions
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<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.
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<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 {
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<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 {
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<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
 ////////////////////////////////////////////////////////////////////////////////
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 <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(())
+}
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()
     };