Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add convenience API to std::process::Command #89004

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions library/std/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,84 @@ impl Command {
pub fn get_current_dir(&self) -> Option<&Path> {
self.inner.get_current_dir()
}

/// Convenience wrapper around [`Command::status`].
///
/// Returns an error if the command exited with non-zero status.
///
/// # Examples
///
/// ```no_run
/// # #![feature(command_easy_api)]
/// use std::process::Command;
///
/// let res = Command::new("cat")
/// .arg("no-such-file.txt")
/// .run();
/// assert!(res.is_err());
/// ```
#[unstable(feature = "command_easy_api", issue = "none")]
pub fn run(&mut self) -> io::Result<()> {
let status = self.status()?;
self.check_status(status)
}

/// Convenience wrapper around [`Command::output`] to get the contents of
/// standard output as [`String`].
///
/// The final newline (`\n`) is stripped from the output. Unlike
/// [`Command::output`], `stderr` is inherited by default.
///
/// Returns an error if the command exited with non-zero status or if the
/// output was not valid UTF-8.
///
/// # Examples
///
/// ```no_run
/// # #![feature(command_easy_api)]
/// use std::process::Command;
/// let output = Command::new("git")
/// .args(["rev-parse", "--short", "1.0.0"])
/// .read_stdout()?;
///
/// assert_eq!(output, "55bd4f8ff2b");
/// # Ok::<(), std::io::Error>(())
/// ```
#[unstable(feature = "command_easy_api", issue = "none")]
pub fn read_stdout(&mut self) -> io::Result<String> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bikeshed: I suggest get_stdout for the name? To me, read_stdout fails to suggest that it also waits for termination.

// FIXME: This shouldn't override the stderr to inherit, and merely use
// a default.
self.stderr(Stdio::inherit());

let output = self.output()?;
self.check_status(output.status)?;
let mut stdout = output.stdout;
if stdout.last() == Some(&b'\n') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use strip.suffix instead? let stdout = stdout.strip_suffix(b'\n').unwrap_or(stdout) or something.

stdout.pop();
}
String::from_utf8(stdout).map_err(|_| {
io::Error::new_const(
io::ErrorKind::InvalidData,
format!("command {:?} produced non-UTF-8 output"),
)
})
}

fn check_status(&self, status: ExitStatus) -> io::Result<()> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this would be usefully pub.

if status.success() {
Ok(())
} else {
Err(io::Error::new_const(
io::ErrorKind::Uncategorized,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think Uncategorized is right. IMO this should get its own ErrorKind. Perhaps you'd like to steal io::ErrorKind::SubprocessFailed from 88528a1 ?

match status.code() {
Some(code) => {
format!("command {:?} exited with non zero status ({})", self, code)
}
None => format!("command {:?} was terminated", self),
},
))
}
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down