@@ -110,6 +110,7 @@ use crate::ffi::OsStr;
110110use crate :: fmt;
111111use crate :: fs;
112112use crate :: io:: { self , Initializer , IoSlice , IoSliceMut } ;
113+ use crate :: num:: NonZeroI32 ;
113114use crate :: path:: Path ;
114115use crate :: str;
115116use crate :: sys:: pipe:: { read2, AnonPipe } ;
@@ -1387,8 +1388,8 @@ impl From<fs::File> for Stdio {
13871388/// An `ExitStatus` represents every possible disposition of a process. On Unix this
13881389/// is the **wait status**. It is *not* simply an *exit status* (a value passed to `exit`).
13891390///
1390- /// For proper error reporting of failed processes, print the value of `ExitStatus` using its
1391- /// implementation of [`Display`](crate::fmt::Display).
1391+ /// For proper error reporting of failed processes, print the value of `ExitStatus` or
1392+ /// `ExitStatusError` using their implementations of [`Display`](crate::fmt::Display).
13921393///
13931394/// [`status`]: Command::status
13941395/// [`wait`]: Child::wait
@@ -1401,6 +1402,29 @@ pub struct ExitStatus(imp::ExitStatus);
14011402impl crate :: sealed:: Sealed for ExitStatus { }
14021403
14031404impl ExitStatus {
1405+ /// Was termination successful? Returns a `Result`.
1406+ ///
1407+ /// # Examples
1408+ ///
1409+ /// ```
1410+ /// #![feature(exit_status_error)]
1411+ /// # if cfg!(unix) {
1412+ /// use std::process::Command;
1413+ ///
1414+ /// let status = Command::new("ls")
1415+ /// .arg("/dev/nonexistent")
1416+ /// .status()
1417+ /// .expect("ls could not be executed");
1418+ ///
1419+ /// println!("ls: {}", status);
1420+ /// status.exit_ok().expect_err("/dev/nonexistent could be listed!");
1421+ /// # } // cfg!(unix)
1422+ /// ```
1423+ #[ unstable( feature = "exit_status_error" , issue = "84908" ) ]
1424+ pub fn exit_ok ( & self ) -> Result < ( ) , ExitStatusError > {
1425+ self . 0 . exit_ok ( ) . map_err ( ExitStatusError )
1426+ }
1427+
14041428 /// Was termination successful? Signal termination is not considered a
14051429 /// success, and success is defined as a zero exit status.
14061430 ///
@@ -1422,7 +1446,7 @@ impl ExitStatus {
14221446 /// ```
14231447 #[ stable( feature = "process" , since = "1.0.0" ) ]
14241448 pub fn success ( & self ) -> bool {
1425- self . 0 . success ( )
1449+ self . 0 . exit_ok ( ) . is_ok ( )
14261450 }
14271451
14281452 /// Returns the exit code of the process, if any.
@@ -1476,6 +1500,114 @@ impl fmt::Display for ExitStatus {
14761500 }
14771501}
14781502
1503+ /// Describes the result of a process after it has failed
1504+ ///
1505+ /// Produced by the [`.exit_ok`](ExitStatus::exit_ok) method on [`ExitStatus`].
1506+ ///
1507+ /// # Examples
1508+ ///
1509+ /// ```
1510+ /// #![feature(exit_status_error)]
1511+ /// # if cfg!(unix) {
1512+ /// use std::process::{Command, ExitStatusError};
1513+ ///
1514+ /// fn run(cmd: &str) -> Result<(),ExitStatusError> {
1515+ /// Command::new(cmd).status().unwrap().exit_ok()?;
1516+ /// Ok(())
1517+ /// }
1518+ ///
1519+ /// run("true").unwrap();
1520+ /// run("false").unwrap_err();
1521+ /// # } // cfg!(unix)
1522+ /// ```
1523+ #[ derive( PartialEq , Eq , Clone , Copy , Debug ) ]
1524+ #[ unstable( feature = "exit_status_error" , issue = "84908" ) ]
1525+ // The definition of imp::ExitStatusError should ideally be such that
1526+ // Result<(), imp::ExitStatusError> has an identical representation to imp::ExitStatus.
1527+ pub struct ExitStatusError ( imp:: ExitStatusError ) ;
1528+
1529+ #[ unstable( feature = "exit_status_error" , issue = "84908" ) ]
1530+ impl ExitStatusError {
1531+ /// Reports the exit code, if applicable, from an `ExitStatusError`.
1532+ ///
1533+ /// In Unix terms the return value is the **exit status**: the value passed to `exit`, if the
1534+ /// process finished by calling `exit`. Note that on Unix the exit status is truncated to 8
1535+ /// bits, and that values that didn't come from a program's call to `exit` may be invented the
1536+ /// runtime system (often, for example, 255, 254, 127 or 126).
1537+ ///
1538+ /// On Unix, this will return `None` if the process was terminated by a signal. If you want to
1539+ /// handle such situations specially, consider using
1540+ /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt) (possibly after getting the
1541+ /// general `ExitStatus` by using [`status()`](ExitStatusError::status).
1542+ ///
1543+ /// If the process finished by calling `exit` with a nonzero value, this will return
1544+ /// that exit status.
1545+ ///
1546+ /// If the error was something else, it will return `None`.
1547+ ///
1548+ /// If the process exited successfully (ie, by calling `exit(0)`), there is no
1549+ /// `ExitStatusError`. So the return value from `ExitStatusError::code()` is always nonzero.
1550+ ///
1551+ /// # Examples
1552+ ///
1553+ /// ```
1554+ /// #![feature(exit_status_error)]
1555+ /// # #[cfg(unix)] {
1556+ /// use std::process::Command;
1557+ ///
1558+ /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err();
1559+ /// assert_eq!(bad.code(), Some(1));
1560+ /// # } // #[cfg(unix)]
1561+ /// ```
1562+ pub fn code ( & self ) -> Option < i32 > {
1563+ self . code_nonzero ( ) . map ( Into :: into)
1564+ }
1565+
1566+ /// Reports the exit code, if applicable, from an `ExitStatusError`, as a `NonZero`
1567+ ///
1568+ /// This is exaclty like [`code()`](Self::code), except that it returns a `NonZeroI32`.
1569+ ///
1570+ /// Plain `code`, returning a plain integer, is provided because is is often more convenient.
1571+ /// The returned value from `code()` is indeed also nonzero; use `code_nonzero()` when you want
1572+ /// a type-level guarantee of nonzeroness.
1573+ ///
1574+ /// # Examples
1575+ ///
1576+ /// ```
1577+ /// #![feature(exit_status_error)]
1578+ /// # if cfg!(unix) {
1579+ /// use std::convert::TryFrom;
1580+ /// use std::num::NonZeroI32;
1581+ /// use std::process::Command;
1582+ ///
1583+ /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err();
1584+ /// assert_eq!(bad.code_nonzero().unwrap(), NonZeroI32::try_from(1).unwrap());
1585+ /// # } // cfg!(unix)
1586+ /// ```
1587+ pub fn code_nonzero ( & self ) -> Option < NonZeroI32 > {
1588+ self . 0 . code ( )
1589+ }
1590+
1591+ /// Converts an `ExitStatusError` (back) to an `ExitStatus`.
1592+ pub fn into_status ( & self ) -> ExitStatus {
1593+ ExitStatus ( self . 0 . into ( ) )
1594+ }
1595+ }
1596+
1597+ #[ unstable( feature = "exit_status_error" , issue = "84908" ) ]
1598+ impl Into < ExitStatus > for ExitStatusError {
1599+ fn into ( self ) -> ExitStatus {
1600+ ExitStatus ( self . 0 . into ( ) )
1601+ }
1602+ }
1603+
1604+ #[ unstable( feature = "exit_status_error" , issue = "84908" ) ]
1605+ impl fmt:: Display for ExitStatusError {
1606+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1607+ self . into_status ( ) . fmt ( f)
1608+ }
1609+ }
1610+
14791611/// This type represents the status code a process can return to its
14801612/// parent under normal termination.
14811613///
0 commit comments