Skip to content

Commit 21d0f91

Browse files
committed
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`.
2 parents 76242ae + 9debe91 commit 21d0f91

File tree

6 files changed

+156
-8
lines changed

6 files changed

+156
-8
lines changed

src/libstd/process.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ use io::prelude::*;
5959

6060
use ffi::OsStr;
6161
use fmt;
62+
use fs;
6263
use io;
6364
use path::Path;
6465
use str;
@@ -544,8 +545,8 @@ impl Command {
544545
/// .expect("ls command failed to start");
545546
/// ```
546547
#[stable(feature = "process", since = "1.0.0")]
547-
pub fn stdin(&mut self, cfg: Stdio) -> &mut Command {
548-
self.inner.stdin(cfg.0);
548+
pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
549+
self.inner.stdin(cfg.into().0);
549550
self
550551
}
551552

@@ -564,8 +565,8 @@ impl Command {
564565
/// .expect("ls command failed to start");
565566
/// ```
566567
#[stable(feature = "process", since = "1.0.0")]
567-
pub fn stdout(&mut self, cfg: Stdio) -> &mut Command {
568-
self.inner.stdout(cfg.0);
568+
pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
569+
self.inner.stdout(cfg.into().0);
569570
self
570571
}
571572

@@ -584,8 +585,8 @@ impl Command {
584585
/// .expect("ls command failed to start");
585586
/// ```
586587
#[stable(feature = "process", since = "1.0.0")]
587-
pub fn stderr(&mut self, cfg: Stdio) -> &mut Command {
588-
self.inner.stderr(cfg.0);
588+
pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
589+
self.inner.stderr(cfg.into().0);
589590
self
590591
}
591592

@@ -753,6 +754,34 @@ impl fmt::Debug for Stdio {
753754
}
754755
}
755756

757+
#[stable(feature = "stdio_from", since = "1.20.0")]
758+
impl From<ChildStdin> for Stdio {
759+
fn from(child: ChildStdin) -> Stdio {
760+
Stdio::from_inner(child.into_inner().into())
761+
}
762+
}
763+
764+
#[stable(feature = "stdio_from", since = "1.20.0")]
765+
impl From<ChildStdout> for Stdio {
766+
fn from(child: ChildStdout) -> Stdio {
767+
Stdio::from_inner(child.into_inner().into())
768+
}
769+
}
770+
771+
#[stable(feature = "stdio_from", since = "1.20.0")]
772+
impl From<ChildStderr> for Stdio {
773+
fn from(child: ChildStderr) -> Stdio {
774+
Stdio::from_inner(child.into_inner().into())
775+
}
776+
}
777+
778+
#[stable(feature = "stdio_from", since = "1.20.0")]
779+
impl From<fs::File> for Stdio {
780+
fn from(file: fs::File) -> Stdio {
781+
Stdio::from_inner(file.into_inner().into())
782+
}
783+
}
784+
756785
/// Describes the result of a process after it has terminated.
757786
///
758787
/// This `struct` is used to represent the exit status of a child process.

src/libstd/sys/redox/process.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,18 @@ impl Stdio {
400400
}
401401
}
402402

403+
impl From<AnonPipe> for Stdio {
404+
fn from(pipe: AnonPipe) -> Stdio {
405+
Stdio::Fd(pipe.into_fd())
406+
}
407+
}
408+
409+
impl From<File> for Stdio {
410+
fn from(file: File) -> Stdio {
411+
Stdio::Fd(file.into_fd())
412+
}
413+
}
414+
403415
impl ChildStdio {
404416
fn fd(&self) -> Option<usize> {
405417
match *self {

src/libstd/sys/unix/process/process_common.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,18 @@ impl Stdio {
315315
}
316316
}
317317

318+
impl From<AnonPipe> for Stdio {
319+
fn from(pipe: AnonPipe) -> Stdio {
320+
Stdio::Fd(pipe.into_fd())
321+
}
322+
}
323+
324+
impl From<File> for Stdio {
325+
fn from(file: File) -> Stdio {
326+
Stdio::Fd(file.into_fd())
327+
}
328+
}
329+
318330
impl ChildStdio {
319331
pub fn fd(&self) -> Option<c_int> {
320332
match *self {

src/libstd/sys/windows/process.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,18 @@ impl Stdio {
306306
}
307307
}
308308

309+
impl From<AnonPipe> for Stdio {
310+
fn from(pipe: AnonPipe) -> Stdio {
311+
Stdio::Handle(pipe.into_handle())
312+
}
313+
}
314+
315+
impl From<File> for Stdio {
316+
fn from(file: File) -> Stdio {
317+
Stdio::Handle(file.into_handle())
318+
}
319+
}
320+
309321
////////////////////////////////////////////////////////////////////////////////
310322
// Processes
311323
////////////////////////////////////////////////////////////////////////////////
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-cross-compile
12+
13+
#![feature(rustc_private)]
14+
15+
extern crate rustc_back;
16+
17+
use std::env;
18+
use std::fs::File;
19+
use std::io;
20+
use std::io::{Read, Write};
21+
use std::process::{Command, Stdio};
22+
23+
use rustc_back::tempdir::TempDir;
24+
25+
fn main() {
26+
if env::args().len() > 1 {
27+
child().unwrap()
28+
} else {
29+
parent().unwrap()
30+
}
31+
}
32+
33+
fn parent() -> io::Result<()> {
34+
let td = TempDir::new("foo").unwrap();
35+
let input = td.path().join("input");
36+
let output = td.path().join("output");
37+
38+
File::create(&input)?.write_all(b"foo\n")?;
39+
40+
// Set up this chain:
41+
// $ me <file | me | me >file
42+
// ... to duplicate each line 8 times total.
43+
44+
let mut child1 = Command::new(env::current_exe()?)
45+
.arg("first")
46+
.stdin(File::open(&input)?) // tests File::into()
47+
.stdout(Stdio::piped())
48+
.spawn()?;
49+
50+
let mut child3 = Command::new(env::current_exe()?)
51+
.arg("third")
52+
.stdin(Stdio::piped())
53+
.stdout(File::create(&output)?) // tests File::into()
54+
.spawn()?;
55+
56+
// Started out of order so we can test both `ChildStdin` and `ChildStdout`.
57+
let mut child2 = Command::new(env::current_exe()?)
58+
.arg("second")
59+
.stdin(child1.stdout.take().unwrap()) // tests ChildStdout::into()
60+
.stdout(child3.stdin.take().unwrap()) // tests ChildStdin::into()
61+
.spawn()?;
62+
63+
assert!(child1.wait()?.success());
64+
assert!(child2.wait()?.success());
65+
assert!(child3.wait()?.success());
66+
67+
let mut data = String::new();
68+
File::open(&output)?.read_to_string(&mut data)?;
69+
for line in data.lines() {
70+
assert_eq!(line, "foo");
71+
}
72+
assert_eq!(data.lines().count(), 8);
73+
Ok(())
74+
}
75+
76+
fn child() -> io::Result<()> {
77+
// double everything
78+
let mut input = vec![];
79+
io::stdin().read_to_end(&mut input)?;
80+
io::stdout().write_all(&input)?;
81+
io::stdout().write_all(&input)?;
82+
Ok(())
83+
}

src/test/run-pass/issue-30490.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ fn main() {
6969
Command::new(name)
7070
.arg("--child")
7171
.stdin(Stdio::inherit())
72-
.stdout(unsafe { FromRawFd::from_raw_fd(libc::STDERR_FILENO) })
73-
.stderr(unsafe { FromRawFd::from_raw_fd(libc::STDOUT_FILENO) })
72+
.stdout(unsafe { Stdio::from_raw_fd(libc::STDERR_FILENO) })
73+
.stderr(unsafe { Stdio::from_raw_fd(libc::STDOUT_FILENO) })
7474
.spawn()
7575
};
7676

0 commit comments

Comments
 (0)