@@ -110,6 +110,7 @@ use crate::ffi::OsStr;
110
110
use crate :: fmt;
111
111
use crate :: fs;
112
112
use crate :: io:: { self , Initializer , IoSlice , IoSliceMut } ;
113
+ use crate :: num:: NonZeroI32 ;
113
114
use crate :: path:: Path ;
114
115
use crate :: str;
115
116
use crate :: sys:: pipe:: { read2, AnonPipe } ;
@@ -1387,8 +1388,8 @@ impl From<fs::File> for Stdio {
1387
1388
/// An `ExitStatus` represents every possible disposition of a process. On Unix this
1388
1389
/// is the **wait status**. It is *not* simply an *exit status* (a value passed to `exit`).
1389
1390
///
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).
1392
1393
///
1393
1394
/// [`status`]: Command::status
1394
1395
/// [`wait`]: Child::wait
@@ -1401,6 +1402,29 @@ pub struct ExitStatus(imp::ExitStatus);
1401
1402
impl crate :: sealed:: Sealed for ExitStatus { }
1402
1403
1403
1404
impl 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
+
1404
1428
/// Was termination successful? Signal termination is not considered a
1405
1429
/// success, and success is defined as a zero exit status.
1406
1430
///
@@ -1422,7 +1446,7 @@ impl ExitStatus {
1422
1446
/// ```
1423
1447
#[ stable( feature = "process" , since = "1.0.0" ) ]
1424
1448
pub fn success ( & self ) -> bool {
1425
- self . 0 . success ( )
1449
+ self . 0 . exit_ok ( ) . is_ok ( )
1426
1450
}
1427
1451
1428
1452
/// Returns the exit code of the process, if any.
@@ -1476,6 +1500,120 @@ impl fmt::Display for ExitStatus {
1476
1500
}
1477
1501
}
1478
1502
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
+
1479
1617
/// This type represents the status code a process can return to its
1480
1618
/// parent under normal termination.
1481
1619
///
0 commit comments