diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 51666c0a3c7f1..15611b86b2234 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -284,6 +284,45 @@ pub enum ErrorKind { #[stable(feature = "out_of_memory_error", since = "1.54.0")] OutOfMemory, + /// Subprocess failed. + /// + /// A subprocess (eg, run by a + /// [`Command`](crate::process::Command)) failed. + /// + /// Often an [`io::Error`](`crate::io::Error`) with this kind wraps an + /// [`ExitStatusError`](crate::process::ExitStatusError), + /// (perhaps via `?` and `Into`), in which case + /// [`io::Error::get_ref`](crate::io::Error::get_ref) or + /// [`std::error::Error::source`](crate::error::Error::source) + /// is the `ExitStatusError`, + /// allowing the subprocess's exit status to be obtained. + /// + /// (The exit code, exit status, or wait status is generally *not* + /// available via + /// [`io::Error::raw_os_error`](crate::io::Error::raw_os_error), + /// since process exit codes are not generally the same as OS + /// error codes..) + /// + /// # Example + /// ``` + /// #![feature(exit_status_error)] + /// use std::process::{Command, ExitStatusError}; + /// + /// fn system(shellcmd: &str) -> Result<(), std::io::Error> { + /// Command::new("sh").args(&["-c",shellcmd]).status()?.exit_ok()?; + /// Ok(()) + /// } + /// + /// # if cfg!(unix) { + /// let err = system("exit 23").unwrap_err(); + /// let exit_error: &ExitStatusError = err.get_ref().unwrap().downcast_ref().unwrap(); + /// assert_eq!(err.to_string(), "process exited unsuccessfully: exit status: 23"); + /// assert_eq!(exit_error.code(), Some(23)); + /// # } + /// ``` + #[unstable(feature = "exit_status_error", issue = "84908")] + SubprocessFailed, + // "Unusual" error kinds which do not correspond simply to (sets // of) OS error codes, should be added just above this comment. // `Other` and `Uncategorised` should remain at the end: @@ -350,6 +389,7 @@ impl ErrorKind { ResourceBusy => "resource busy", StaleNetworkFileHandle => "stale network file handle", StorageFull => "no storage space", + SubprocessFailed => "subprocess failed", TimedOut => "timed out", TooManyLinks => "too many links", Uncategorized => "uncategorized error", diff --git a/library/std/src/process.rs b/library/std/src/process.rs index c9b21fcf9c6d2..48f4871424d21 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1530,6 +1530,9 @@ impl crate::sealed::Sealed for ExitStatusError {} /// /// Produced by the [`.exit_ok`](ExitStatus::exit_ok) method on [`ExitStatus`]. /// +/// Implements [`Into`], producing an [`io::Error`] +/// of kind [`Subprocessfailed`](io::ErrorKind::SubprocessFailed). +/// /// # Examples /// /// ``` @@ -1636,6 +1639,13 @@ impl fmt::Display for ExitStatusError { #[unstable(feature = "exit_status_error", issue = "84908")] impl crate::error::Error for ExitStatusError {} +#[unstable(feature = "exit_status_error", issue = "84908")] +impl From for io::Error { + fn from(ese: ExitStatusError) -> io::Error { + io::Error::new(io::ErrorKind::SubprocessFailed, ese) + } +} + /// This type represents the status code a process can return to its /// parent under normal termination. /// diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index bc71c150550a4..72186f8953f7c 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -129,7 +129,11 @@ fn test_process_status() { #[test] fn test_process_output_fail_to_start() { match Command::new("/no-binary-by-this-name-should-exist").output() { - Err(e) => assert_eq!(e.kind(), ErrorKind::NotFound), + Err(e) => { + assert_eq!(e.kind(), ErrorKind::NotFound); + #[cfg(unix)] // Feel free to adjust/disable if this varies on some platform + assert_eq!(e.to_string(), "No such file or directory (os error 2)"); + } Ok(..) => panic!(), } }