From b50c1bbb0e2b73aed1a3cfc8da5e1082ddb21040 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Wed, 3 Mar 2021 11:36:07 +0000 Subject: [PATCH 1/7] windows: provide NonZeroDWORD Signed-off-by: Ian Jackson --- library/std/src/sys/windows/c.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 7ea6048e94a88..e91c489361ea6 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -4,6 +4,7 @@ #![cfg_attr(test, allow(dead_code))] #![unstable(issue = "none", feature = "windows_c")] +use crate::os::raw::NonZero_c_ulong; use crate::os::raw::{c_char, c_int, c_long, c_longlong, c_uint, c_ulong, c_ushort}; use crate::ptr; @@ -13,6 +14,7 @@ pub use self::EXCEPTION_DISPOSITION::*; pub use self::FILE_INFO_BY_HANDLE_CLASS::*; pub type DWORD = c_ulong; +pub type NonZeroDWORD = NonZero_c_ulong; pub type HANDLE = LPVOID; pub type HINSTANCE = HANDLE; pub type HMODULE = HINSTANCE; From e893089ea066ce2b339543ac8e59b4e0ca8c44d3 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Wed, 3 Mar 2021 12:17:16 +0000 Subject: [PATCH 2/7] Provide ExitStatusError Closes #73125 This is in pursuance of Issue #73127 Consider adding #[must_use] to std::process::ExitStatus In MR #81452 Add #[must_use] to [...] process::ExitStatus we concluded that the existing arrangements in are too awkward so adding that #[must_use] is blocked on improving the ergonomics. I wrote a mini-RFC-style discusion of the approach in https://github.com/rust-lang/rust/issues/73125#issuecomment-771092741 Signed-off-by: Ian Jackson --- library/std/src/process.rs | 138 +++++++++++++++++- library/std/src/sys/unix/process/mod.rs | 2 +- .../src/sys/unix/process/process_fuchsia.rs | 26 +++- .../std/src/sys/unix/process/process_unix.rs | 31 +++- library/std/src/sys/unsupported/process.rs | 18 ++- library/std/src/sys/windows/process.rs | 25 +++- 6 files changed, 227 insertions(+), 13 deletions(-) diff --git a/library/std/src/process.rs b/library/std/src/process.rs index b45c620fd0b06..6020ecf685366 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -110,6 +110,7 @@ use crate::ffi::OsStr; use crate::fmt; use crate::fs; use crate::io::{self, Initializer, IoSlice, IoSliceMut}; +use crate::num::NonZeroI32; use crate::path::Path; use crate::str; use crate::sys::pipe::{read2, AnonPipe}; @@ -1387,8 +1388,8 @@ impl From for Stdio { /// An `ExitStatus` represents every possible disposition of a process. On Unix this /// is the **wait status**. It is *not* simply an *exit status* (a value passed to `exit`). /// -/// For proper error reporting of failed processes, print the value of `ExitStatus` using its -/// implementation of [`Display`](crate::fmt::Display). +/// For proper error reporting of failed processes, print the value of `ExitStatus` or +/// `ExitStatusError` using their implementations of [`Display`](crate::fmt::Display). /// /// [`status`]: Command::status /// [`wait`]: Child::wait @@ -1401,6 +1402,29 @@ pub struct ExitStatus(imp::ExitStatus); impl crate::sealed::Sealed for ExitStatus {} impl ExitStatus { + /// Was termination successful? Returns a `Result`. + /// + /// # Examples + /// + /// ``` + /// #![feature(exit_status_error)] + /// # if cfg!(unix) { + /// use std::process::Command; + /// + /// let status = Command::new("ls") + /// .arg("/dev/nonexistent") + /// .status() + /// .expect("ls could not be executed"); + /// + /// println!("ls: {}", status); + /// status.exit_ok().expect_err("/dev/nonexistent could be listed!"); + /// # } // cfg!(unix) + /// ``` + #[unstable(feature = "exit_status_error", issue = "84908")] + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + self.0.exit_ok().map_err(ExitStatusError) + } + /// Was termination successful? Signal termination is not considered a /// success, and success is defined as a zero exit status. /// @@ -1422,7 +1446,7 @@ impl ExitStatus { /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn success(&self) -> bool { - self.0.success() + self.0.exit_ok().is_ok() } /// Returns the exit code of the process, if any. @@ -1476,6 +1500,114 @@ impl fmt::Display for ExitStatus { } } +/// Describes the result of a process after it has failed +/// +/// Produced by the [`.exit_ok`](ExitStatus::exit_ok) method on [`ExitStatus`]. +/// +/// # Examples +/// +/// ``` +/// #![feature(exit_status_error)] +/// # if cfg!(unix) { +/// use std::process::{Command, ExitStatusError}; +/// +/// fn run(cmd: &str) -> Result<(),ExitStatusError> { +/// Command::new(cmd).status().unwrap().exit_ok()?; +/// Ok(()) +/// } +/// +/// run("true").unwrap(); +/// run("false").unwrap_err(); +/// # } // cfg!(unix) +/// ``` +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[unstable(feature = "exit_status_error", issue = "84908")] +// The definition of imp::ExitStatusError should ideally be such that +// Result<(), imp::ExitStatusError> has an identical representation to imp::ExitStatus. +pub struct ExitStatusError(imp::ExitStatusError); + +#[unstable(feature = "exit_status_error", issue = "84908")] +impl ExitStatusError { + /// Reports the exit code, if applicable, from an `ExitStatusError`. + /// + /// In Unix terms the return value is the **exit status**: the value passed to `exit`, if the + /// process finished by calling `exit`. Note that on Unix the exit status is truncated to 8 + /// bits, and that values that didn't come from a program's call to `exit` may be invented the + /// runtime system (often, for example, 255, 254, 127 or 126). + /// + /// On Unix, this will return `None` if the process was terminated by a signal. If you want to + /// handle such situations specially, consider using + /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt) (possibly after getting the + /// general `ExitStatus` by using [`status()`](ExitStatusError::status). + /// + /// If the process finished by calling `exit` with a nonzero value, this will return + /// that exit status. + /// + /// If the error was something else, it will return `None`. + /// + /// If the process exited successfully (ie, by calling `exit(0)`), there is no + /// `ExitStatusError`. So the return value from `ExitStatusError::code()` is always nonzero. + /// + /// # Examples + /// + /// ``` + /// #![feature(exit_status_error)] + /// # #[cfg(unix)] { + /// use std::process::Command; + /// + /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err(); + /// assert_eq!(bad.code(), Some(1)); + /// # } // #[cfg(unix)] + /// ``` + pub fn code(&self) -> Option { + self.code_nonzero().map(Into::into) + } + + /// Reports the exit code, if applicable, from an `ExitStatusError`, as a `NonZero` + /// + /// This is exaclty like [`code()`](Self::code), except that it returns a `NonZeroI32`. + /// + /// Plain `code`, returning a plain integer, is provided because is is often more convenient. + /// The returned value from `code()` is indeed also nonzero; use `code_nonzero()` when you want + /// a type-level guarantee of nonzeroness. + /// + /// # Examples + /// + /// ``` + /// #![feature(exit_status_error)] + /// # if cfg!(unix) { + /// use std::convert::TryFrom; + /// use std::num::NonZeroI32; + /// use std::process::Command; + /// + /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err(); + /// assert_eq!(bad.code_nonzero().unwrap(), NonZeroI32::try_from(1).unwrap()); + /// # } // cfg!(unix) + /// ``` + pub fn code_nonzero(&self) -> Option { + self.0.code() + } + + /// Converts an `ExitStatusError` (back) to an `ExitStatus`. + pub fn into_status(&self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +#[unstable(feature = "exit_status_error", issue = "84908")] +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +#[unstable(feature = "exit_status_error", issue = "84908")] +impl fmt::Display for ExitStatusError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.into_status().fmt(f) + } +} + /// This type represents the status code a process can return to its /// parent under normal termination. /// diff --git a/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs index f67c70c01772f..b5a19ed54a2f2 100644 --- a/library/std/src/sys/unix/process/mod.rs +++ b/library/std/src/sys/unix/process/mod.rs @@ -1,5 +1,5 @@ pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes}; -pub use self::process_inner::{ExitStatus, Process}; +pub use self::process_inner::{ExitStatus, ExitStatusError, Process}; pub use crate::ffi::OsString as EnvKey; pub use crate::sys_common::process::CommandEnvs; diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs index b19ad4ccdc777..507abb27871bf 100644 --- a/library/std/src/sys/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/unix/process/process_fuchsia.rs @@ -1,7 +1,8 @@ -use crate::convert::TryInto; +use crate::convert::{TryFrom, TryInto}; use crate::fmt; use crate::io; use crate::mem; +use crate::num::{NonZeroI32, NonZeroI64}; use crate::ptr; use crate::sys::process::process_common::*; @@ -236,8 +237,11 @@ impl Process { pub struct ExitStatus(i64); impl ExitStatus { - pub fn success(&self) -> bool { - self.code() == Some(0) + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + match NonZeroI64::try_from(self.0) { + /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), + /* was zero, couldn't convert */ Err(_) => Ok(()), + } } pub fn code(&self) -> Option { @@ -306,3 +310,19 @@ impl fmt::Display for ExitStatus { write!(f, "exit code: {}", self.0) } } + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatusError(NonZeroI64); + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option { + // fixme: affected by the same bug as ExitStatus::code() + ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) + } +} diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index ed9044382a898..f711b4cc68950 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -1,7 +1,9 @@ -use crate::convert::TryInto; +use crate::convert::{TryFrom, TryInto}; use crate::fmt; use crate::io::{self, Error, ErrorKind}; use crate::mem; +use crate::num::NonZeroI32; +use crate::os::raw::NonZero_c_int; use crate::ptr; use crate::sys; use crate::sys::cvt; @@ -490,8 +492,16 @@ impl ExitStatus { libc::WIFEXITED(self.0) } - pub fn success(&self) -> bool { - self.code() == Some(0) + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is + // true on all actual versios of Unix, is widely assumed, and is specified in SuS + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not + // true for a platform pretending to be Unix, the tests (our doctests, and also + // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. + match NonZero_c_int::try_from(self.0) { + /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), + /* was zero, couldn't convert */ Err(_) => Ok(()), + } } pub fn code(&self) -> Option { @@ -546,6 +556,21 @@ impl fmt::Display for ExitStatus { } } +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatusError(NonZero_c_int); + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option { + ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) + } +} + #[cfg(test)] #[path = "process_unix/tests.rs"] mod tests; diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs index 38ac0a1ddd5f9..7846e43cfb53e 100644 --- a/library/std/src/sys/unsupported/process.rs +++ b/library/std/src/sys/unsupported/process.rs @@ -2,6 +2,7 @@ use crate::ffi::OsStr; use crate::fmt; use crate::io; use crate::marker::PhantomData; +use crate::num::NonZeroI32; use crate::path::Path; use crate::sys::fs::File; use crate::sys::pipe::AnonPipe; @@ -97,7 +98,7 @@ impl fmt::Debug for Command { pub struct ExitStatus(!); impl ExitStatus { - pub fn success(&self) -> bool { + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { self.0 } @@ -134,6 +135,21 @@ impl fmt::Display for ExitStatus { } } +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatusError(ExitStatus); + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + self.0.0 + } +} + +impl ExitStatusError { + pub fn code(self) -> Option { + self.0.0 + } +} + #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct ExitCode(bool); diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index a5799606142ec..81dbea4a06739 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -5,6 +5,7 @@ mod tests; use crate::borrow::Borrow; use crate::collections::BTreeMap; +use crate::convert::{TryFrom, TryInto}; use crate::env; use crate::env::split_paths; use crate::ffi::{OsStr, OsString}; @@ -12,10 +13,12 @@ use crate::fmt; use crate::fs; use crate::io::{self, Error, ErrorKind}; use crate::mem; +use crate::num::NonZeroI32; use crate::os::windows::ffi::OsStrExt; use crate::path::Path; use crate::ptr; use crate::sys::c; +use crate::sys::c::NonZeroDWORD; use crate::sys::cvt; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; @@ -376,8 +379,11 @@ impl Process { pub struct ExitStatus(c::DWORD); impl ExitStatus { - pub fn success(&self) -> bool { - self.0 == 0 + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + match NonZeroDWORD::try_from(self.0) { + /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), + /* was zero, couldn't convert */ Err(_) => Ok(()), + } } pub fn code(&self) -> Option { Some(self.0 as i32) @@ -406,6 +412,21 @@ impl fmt::Display for ExitStatus { } } +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatusError(c::NonZeroDWORD); + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option { + Some((u32::from(self.0) as i32).try_into().unwrap()) + } +} + #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct ExitCode(c::DWORD); From 60a4d9612d5cd5dde600bbf7a3cb5431f55de670 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Wed, 24 Feb 2021 14:52:16 +0000 Subject: [PATCH 3/7] unix: impl ExitStatusExt for ExitStatusError It is unergnomic to have to say things like bad.into_status().signal() Implementing `ExitStatusExt` for `ExitStatusError` fixes this. Unfortunately it does mean making a previously-infallible method capable of panicing, although of course the existing impl remains infallible. The alternative would be a whole new `ExitStatusErrorExt` trait. `::into_raw()` is not particularly ergonomic to call because of the often-required type annotation. See for example the code in the test case in library/std/src/sys/unix/process/process_unix/tests.rs Perhaps we should provide equivalent free functions for `ExitStatus` and `ExitStatusExt` in std::os::unix::process and maybe deprecate this trait method. But I think that is for the future. Signed-off-by: Ian Jackson --- library/std/src/os/unix/process.rs | 77 +++++++++++++++++++++++++++--- library/std/src/process.rs | 9 ++-- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 355855bcd10e2..21da8ba15de01 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -195,28 +195,62 @@ impl CommandExt for process::Command { } } -/// Unix-specific extensions to [`process::ExitStatus`]. +/// Unix-specific extensions to [`process::ExitStatus`] and +/// [`ExitStatusError`](process::ExitStatusError). /// -/// On Unix, `ExitStatus` **does not necessarily represent an exit status**, as passed to the -/// `exit` system call or returned by [`ExitStatus::code()`](crate::process::ExitStatus::code). -/// It represents **any wait status**, as returned by one of the `wait` family of system calls. +/// On Unix, `ExitStatus` and `ExitStatusError` **do not necessarily represent an exit status**, as +/// passed to the `exit` system call or returned by +/// [`ExitStatus::code()`](crate::process::ExitStatus::code). They represents **any wait status** +/// (or any nonzero wait status, respectively), as returned by one of the `wait` family of system +/// calls. /// -/// This is because a Unix wait status (a Rust `ExitStatus`) can represent a Unix exit status, but -/// can also represent other kinds of process event. +/// A Unix wait status (a Rust `ExitStatus`) can represent a Unix exit status, but can also +/// represent other kinds of process event. /// /// This trait is sealed: it cannot be implemented outside the standard library. /// This is so that future additional methods are not breaking changes. #[stable(feature = "rust1", since = "1.0.0")] pub trait ExitStatusExt: Sealed { - /// Creates a new `ExitStatus` from the raw underlying integer status value from `wait` + /// Creates a new `ExitStatus` or `ExitStatusError` from the raw underlying integer status + /// value from `wait` /// /// The value should be a **wait status, not an exit status**. + /// + /// # Panics + /// + /// Panics on an attempt to make an `ExitStatusError` from a wait status of `0`. + /// + /// Making an `ExitStatus` always succeds and never panics. #[stable(feature = "exit_status_from", since = "1.12.0")] fn from_raw(raw: i32) -> Self; /// If the process was terminated by a signal, returns that signal. /// /// In other words, if `WIFSIGNALED`, this returns `WTERMSIG`. + /// + /// # Examples + /// ``` + /// #![feature(exit_status_error)] + /// use std::process::{Command, ExitStatusError}; + /// use std::os::unix::process::ExitStatusExt; + /// + /// fn run(script: &str) -> Result<(), ExitStatusError> { + /// Command::new("sh").args(&["-ec",script]) + /// .status().expect("failed to fork/exec sh") + /// .exit_ok() + /// .or_else(|bad| { + /// if bad.signal() == Some(13) /*PIPE*/ { + /// Ok(()) + /// } else { + /// Err(bad) + /// } + /// }) + /// } + /// + /// run("exit").unwrap(); + /// run("kill -PIPE $$").unwrap(); + /// run("exit 42").unwrap_err(); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn signal(&self) -> Option; @@ -272,6 +306,35 @@ impl ExitStatusExt for process::ExitStatus { } } +#[unstable(feature = "exit_status_error", issue = "84908")] +impl ExitStatusExt for process::ExitStatusError { + fn from_raw(raw: i32) -> Self { + process::ExitStatus::from_raw(raw) + .exit_ok() + .expect_err("::from_raw(0) but zero is not an error") + } + + fn signal(&self) -> Option { + self.into_status().signal() + } + + fn core_dumped(&self) -> bool { + self.into_status().core_dumped() + } + + fn stopped_signal(&self) -> Option { + self.into_status().stopped_signal() + } + + fn continued(&self) -> bool { + self.into_status().continued() + } + + fn into_raw(self) -> i32 { + self.into_status().into_raw() + } +} + #[stable(feature = "process_extensions", since = "1.2.0")] impl FromRawFd for process::Stdio { #[inline] diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 6020ecf685366..c55cc18b2cff5 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1500,6 +1500,10 @@ impl fmt::Display for ExitStatus { } } +/// Allows extension traits within `std`. +#[unstable(feature = "sealed", issue = "none")] +impl crate::sealed::Sealed for ExitStatusError {} + /// Describes the result of a process after it has failed /// /// Produced by the [`.exit_ok`](ExitStatus::exit_ok) method on [`ExitStatus`]. @@ -1536,9 +1540,8 @@ impl ExitStatusError { /// runtime system (often, for example, 255, 254, 127 or 126). /// /// On Unix, this will return `None` if the process was terminated by a signal. If you want to - /// handle such situations specially, consider using - /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt) (possibly after getting the - /// general `ExitStatus` by using [`status()`](ExitStatusError::status). + /// handle such situations specially, consider using methods from + /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt). /// /// If the process finished by calling `exit` with a nonzero value, this will return /// that exit status. From 8832b0a81cd24aa66111a60453375f7833303987 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 16 Mar 2021 15:17:27 +0000 Subject: [PATCH 4/7] Fix typo in doc Co-authored-by: Josh Triplett --- library/std/src/process.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/process.rs b/library/std/src/process.rs index c55cc18b2cff5..e9d807134003e 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1536,7 +1536,7 @@ impl ExitStatusError { /// /// In Unix terms the return value is the **exit status**: the value passed to `exit`, if the /// process finished by calling `exit`. Note that on Unix the exit status is truncated to 8 - /// bits, and that values that didn't come from a program's call to `exit` may be invented the + /// bits, and that values that didn't come from a program's call to `exit` may be invented by the /// runtime system (often, for example, 255, 254, 127 or 126). /// /// On Unix, this will return `None` if the process was terminated by a signal. If you want to From bb4ef68639a0a9bae88c96d9b40207f8cdbbe1ee Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 4 May 2021 14:31:48 +0100 Subject: [PATCH 5/7] ExitStatusError: Be more verbose in Display impl Co-authored-by: Jane Lusby --- library/std/src/process.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/process.rs b/library/std/src/process.rs index e9d807134003e..9252aba903f51 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1607,7 +1607,7 @@ impl Into for ExitStatusError { #[unstable(feature = "exit_status_error", issue = "84908")] impl fmt::Display for ExitStatusError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.into_status().fmt(f) + write!(f, "process exited unsuccessfully: {}", self.into_status()) } } From 46871539e47f04a9c255213bffa909d7556d1448 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Wed, 5 May 2021 01:29:52 +0100 Subject: [PATCH 6/7] impl crate::error::Error for ExitStatusError Signed-off-by: Ian Jackson --- library/std/src/process.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 9252aba903f51..6903ba9056089 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1611,6 +1611,9 @@ impl fmt::Display for ExitStatusError { } } +#[unstable(feature = "exit_status_error", issue = "84908")] +impl crate::error::Error for ExitStatusError {} + /// This type represents the status code a process can return to its /// parent under normal termination. /// From 26c782b8e7f939a00f889dfb3f1e969fc8f8c79d Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 11 May 2021 18:09:03 +0100 Subject: [PATCH 7/7] ExitStatusError: Remove mentions in stable docs We should revert this commit when this is stabilised. Signed-off-by: Ian Jackson --- library/std/src/os/unix/process.rs | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 21da8ba15de01..8b98a22f768e1 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -198,10 +198,10 @@ impl CommandExt for process::Command { /// Unix-specific extensions to [`process::ExitStatus`] and /// [`ExitStatusError`](process::ExitStatusError). /// -/// On Unix, `ExitStatus` and `ExitStatusError` **do not necessarily represent an exit status**, as +/// On Unix, `ExitStatus` **does not necessarily represent an exit status**, as /// passed to the `exit` system call or returned by -/// [`ExitStatus::code()`](crate::process::ExitStatus::code). They represents **any wait status** -/// (or any nonzero wait status, respectively), as returned by one of the `wait` family of system +/// [`ExitStatus::code()`](crate::process::ExitStatus::code). It represents **any wait status** +/// as returned by one of the `wait` family of system /// calls. /// /// A Unix wait status (a Rust `ExitStatus`) can represent a Unix exit status, but can also @@ -227,30 +227,6 @@ pub trait ExitStatusExt: Sealed { /// If the process was terminated by a signal, returns that signal. /// /// In other words, if `WIFSIGNALED`, this returns `WTERMSIG`. - /// - /// # Examples - /// ``` - /// #![feature(exit_status_error)] - /// use std::process::{Command, ExitStatusError}; - /// use std::os::unix::process::ExitStatusExt; - /// - /// fn run(script: &str) -> Result<(), ExitStatusError> { - /// Command::new("sh").args(&["-ec",script]) - /// .status().expect("failed to fork/exec sh") - /// .exit_ok() - /// .or_else(|bad| { - /// if bad.signal() == Some(13) /*PIPE*/ { - /// Ok(()) - /// } else { - /// Err(bad) - /// } - /// }) - /// } - /// - /// run("exit").unwrap(); - /// run("kill -PIPE $$").unwrap(); - /// run("exit 42").unwrap_err(); - /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn signal(&self) -> Option;