@@ -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,120 @@ impl fmt::Display for ExitStatus {
14761500 }
14771501}
14781502
1503+ /// Allows extension traits within `std`.
1504+ #[ unstable( feature = "sealed" , issue = "none" ) ]
1505+ impl crate :: sealed:: Sealed for ExitStatusError { }
1506+
1507+ /// Describes the result of a process after it has failed
1508+ ///
1509+ /// Produced by the [`.exit_ok`](ExitStatus::exit_ok) method on [`ExitStatus`].
1510+ ///
1511+ /// # Examples
1512+ ///
1513+ /// ```
1514+ /// #![feature(exit_status_error)]
1515+ /// # if cfg!(unix) {
1516+ /// use std::process::{Command, ExitStatusError};
1517+ ///
1518+ /// fn run(cmd: &str) -> Result<(),ExitStatusError> {
1519+ /// Command::new(cmd).status().unwrap().exit_ok()?;
1520+ /// Ok(())
1521+ /// }
1522+ ///
1523+ /// run("true").unwrap();
1524+ /// run("false").unwrap_err();
1525+ /// # } // cfg!(unix)
1526+ /// ```
1527+ #[ derive( PartialEq , Eq , Clone , Copy , Debug ) ]
1528+ #[ unstable( feature = "exit_status_error" , issue = "84908" ) ]
1529+ // The definition of imp::ExitStatusError should ideally be such that
1530+ // Result<(), imp::ExitStatusError> has an identical representation to imp::ExitStatus.
1531+ pub struct ExitStatusError ( imp:: ExitStatusError ) ;
1532+
1533+ #[ unstable( feature = "exit_status_error" , issue = "84908" ) ]
1534+ impl ExitStatusError {
1535+ /// Reports the exit code, if applicable, from an `ExitStatusError`.
1536+ ///
1537+ /// In Unix terms the return value is the **exit status**: the value passed to `exit`, if the
1538+ /// process finished by calling `exit`. Note that on Unix the exit status is truncated to 8
1539+ /// bits, and that values that didn't come from a program's call to `exit` may be invented by the
1540+ /// runtime system (often, for example, 255, 254, 127 or 126).
1541+ ///
1542+ /// On Unix, this will return `None` if the process was terminated by a signal. If you want to
1543+ /// handle such situations specially, consider using methods from
1544+ /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt).
1545+ ///
1546+ /// If the process finished by calling `exit` with a nonzero value, this will return
1547+ /// that exit status.
1548+ ///
1549+ /// If the error was something else, it will return `None`.
1550+ ///
1551+ /// If the process exited successfully (ie, by calling `exit(0)`), there is no
1552+ /// `ExitStatusError`. So the return value from `ExitStatusError::code()` is always nonzero.
1553+ ///
1554+ /// # Examples
1555+ ///
1556+ /// ```
1557+ /// #![feature(exit_status_error)]
1558+ /// # #[cfg(unix)] {
1559+ /// use std::process::Command;
1560+ ///
1561+ /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err();
1562+ /// assert_eq!(bad.code(), Some(1));
1563+ /// # } // #[cfg(unix)]
1564+ /// ```
1565+ pub fn code ( & self ) -> Option < i32 > {
1566+ self . code_nonzero ( ) . map ( Into :: into)
1567+ }
1568+
1569+ /// Reports the exit code, if applicable, from an `ExitStatusError`, as a `NonZero`
1570+ ///
1571+ /// This is exaclty like [`code()`](Self::code), except that it returns a `NonZeroI32`.
1572+ ///
1573+ /// Plain `code`, returning a plain integer, is provided because is is often more convenient.
1574+ /// The returned value from `code()` is indeed also nonzero; use `code_nonzero()` when you want
1575+ /// a type-level guarantee of nonzeroness.
1576+ ///
1577+ /// # Examples
1578+ ///
1579+ /// ```
1580+ /// #![feature(exit_status_error)]
1581+ /// # if cfg!(unix) {
1582+ /// use std::convert::TryFrom;
1583+ /// use std::num::NonZeroI32;
1584+ /// use std::process::Command;
1585+ ///
1586+ /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err();
1587+ /// assert_eq!(bad.code_nonzero().unwrap(), NonZeroI32::try_from(1).unwrap());
1588+ /// # } // cfg!(unix)
1589+ /// ```
1590+ pub fn code_nonzero ( & self ) -> Option < NonZeroI32 > {
1591+ self . 0 . code ( )
1592+ }
1593+
1594+ /// Converts an `ExitStatusError` (back) to an `ExitStatus`.
1595+ pub fn into_status ( & self ) -> ExitStatus {
1596+ ExitStatus ( self . 0 . into ( ) )
1597+ }
1598+ }
1599+
1600+ #[ unstable( feature = "exit_status_error" , issue = "84908" ) ]
1601+ impl Into < ExitStatus > for ExitStatusError {
1602+ fn into ( self ) -> ExitStatus {
1603+ ExitStatus ( self . 0 . into ( ) )
1604+ }
1605+ }
1606+
1607+ #[ unstable( feature = "exit_status_error" , issue = "84908" ) ]
1608+ impl fmt:: Display for ExitStatusError {
1609+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1610+ write ! ( f, "process exited unsuccessfully: {}" , self . into_status( ) )
1611+ }
1612+ }
1613+
1614+ #[ unstable( feature = "exit_status_error" , issue = "84908" ) ]
1615+ impl crate :: error:: Error for ExitStatusError { }
1616+
14791617/// This type represents the status code a process can return to its
14801618/// parent under normal termination.
14811619///
0 commit comments