]> git.lizzy.rs Git - rust.git/commitdiff
Add conversions from File and Child* handles to Stdio
authorJosh Stone <jistone@redhat.com>
Tue, 6 Jun 2017 22:42:55 +0000 (15:42 -0700)
committerJosh Stone <jistone@redhat.com>
Tue, 6 Jun 2017 22:42:55 +0000 (15:42 -0700)
`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`.

src/libstd/process.rs
src/libstd/sys/redox/process.rs
src/libstd/sys/unix/process/process_common.rs
src/libstd/sys/windows/process.rs
src/test/run-pass-fulldeps/stdio-from.rs [new file with mode: 0644]
src/test/run-pass/issue-30490.rs

index da64704efbaa9452ec78a3976b9c043803bbb170..4c6d88c0ae88900defb49bb1a02eaa4137506658 100644 (file)
@@ -59,6 +59,7 @@
 
 use ffi::OsStr;
 use fmt;
+use fs;
 use io;
 use path::Path;
 use str;
@@ -544,8 +545,8 @@ pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut 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 @@ pub fn stdin(&mut self, cfg: Stdio) -> &mut 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 @@ pub fn stdout(&mut self, cfg: Stdio) -> &mut 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 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
+#[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.
index 95e9438cd719121bc5eca9ece06e87ca87489855..62d873d257d8ffcd7651af05640a1266b667d5b1 100644 (file)
@@ -400,6 +400,18 @@ fn to_child_stdio(&self, readable: bool)
     }
 }
 
+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 {
index e9f41009064ca43ea267861b2bfe76cc8fd64718..32fcee1e461954e6195c43c4c1a7dfae424e6b4f 100644 (file)
@@ -315,6 +315,18 @@ pub fn to_child_stdio(&self, readable: bool)
     }
 }
 
+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 {
index 0bd0ce73138d6109224070ffffbffa097078804b..0d1766d5aec6d91b967cd5ff20a45c30011fdb66 100644 (file)
@@ -306,6 +306,18 @@ fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option<AnonPipe>)
     }
 }
 
+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 (file)
index 0000000..f64bbf9
--- /dev/null
@@ -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(())
+}
index 035911302cf2499e1fe9d4985c55093734b2ba85..7658abc00c599636da09e54ba515c4acaece3fc0 100644 (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()
     };