1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
/// Process exit code.
///
/// Common exit codes:
/// - [`Code::SUCCESS`]
/// - [`Code::FAILURE`]
/// - [`bash::USAGE`][crate::bash::USAGE]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Code(i32);
/// # Create a [`Code`]
impl Code {
/// The process exited successfully.
pub const SUCCESS: Code = Code(0);
/// Generic failure.
pub const FAILURE: Code = Code(1);
/// Create a custom error code
pub const fn new(code: i32) -> Self {
Self(code)
}
/// Converts [`std::process::ExitStatus`] to [`Code`].
///
/// On Unix, if the process was terminated by a fatal signal, the corresponding
/// signal exit code is returned.
#[inline]
pub fn from_status(status: std::process::ExitStatus) -> Self {
Self::from(status)
}
}
/// # Bubble up the exit [`Code`]
impl Code {
/// [`exit`][std::process::exit] now!
#[inline]
pub fn process_exit(self) -> ! {
std::process::exit(self.as_raw())
}
/// Convert to [`Result`][std::result::Result]
#[inline]
pub fn ok(self) -> crate::ExitResult {
if self.as_raw() == Self::SUCCESS.as_raw() {
Ok(())
} else {
Err(crate::Exit::new(self))
}
}
/// Convert to [`Exit`][crate::Exit] error type
#[inline]
pub fn as_exit(self) -> crate::Exit {
crate::Exit::new(self)
}
/// Add user-visible message (like an [`Error`][std::error::Error])
#[inline]
pub fn with_message<D: std::fmt::Display + 'static>(self, msg: D) -> crate::Exit {
self.as_exit().with_message(msg)
}
}
/// # Introspection and Integration
impl Code {
/// Convert to [`ExitCode][std::process::ExitCode]
#[inline]
pub fn as_exit_code(self) -> Option<std::process::ExitCode> {
self.as_portable().map(|c| c.into())
}
/// Convert to raw value
#[inline]
pub const fn as_raw(self) -> i32 {
self.0
}
/// Convert to portable, raw value
#[inline]
pub const fn as_portable(self) -> Option<u8> {
if self.is_portable() {
Some(self.as_raw() as u8)
} else {
None
}
}
/// Determines if the provided [`std::process::ExitStatus`] was successful.
///
/// Example:
///
/// ```
/// use std::process;
///
/// let exit_status = process::Command::new("true")
/// .status()
/// .expect("failed to run true(1)");
/// assert!(proc_exit::Code::from_status(exit_status).is_ok());
/// ```
///
#[inline]
pub const fn is_ok(self) -> bool {
self.as_raw() == Self::SUCCESS.as_raw()
}
/// Determines if the provided [`std::process::ExitStatus`] was unsuccessful.
///
/// Example:
///
/// ```
/// use std::process;
///
/// let exit_status = process::Command::new("false")
/// .status()
/// .expect("failed to run false(1)");
/// assert!(proc_exit::Code::from_status(exit_status).is_err());
/// ```
///
#[inline]
pub const fn is_err(self) -> bool {
!self.is_ok()
}
/// Test if provided exit code is portable across platforms.
///
/// While Windows has wider types for return codes, Unix OS's tend to only support 8-bits,
/// stripping off the higher order bits.
#[inline]
pub const fn is_portable(self) -> bool {
0 <= self.as_raw() && self.as_raw() <= 255
}
}
impl Default for Code {
#[inline]
fn default() -> Self {
// Chosen to allow `coerce().unwrap_or_default`
Self::FAILURE
}
}
/// Converts an `i32` primitive integer to an exit code.
impl From<i32> for Code {
#[inline]
fn from(n: i32) -> Self {
Self(n)
}
}
/// Converts [`std::process::ExitStatus`] to an exit code by looking at its
/// [`ExitStatus::code()`] value.
///
/// On Unix, if the process was terminated by a fatal signal, the corresponding
/// signal exit code is returned.
///
/// [`std::process::ExitStatus`]:
/// https://doc.rust-lang.org/std/process/struct.ExitStatus.html
/// [`ExitStatus::code()`]:
/// https://doc.rust-lang.org/std/process/struct.ExitStatus.html#method.code
impl From<std::process::ExitStatus> for Code {
#[inline]
fn from(status: std::process::ExitStatus) -> Self {
let n = platform_exit_code(status).unwrap_or(Code::default().0);
From::from(n)
}
}
#[cfg(target_family = "unix")]
#[inline]
fn platform_exit_code(status: std::process::ExitStatus) -> Option<i32> {
use std::os::unix::process::ExitStatusExt;
status.code().or_else(|| status.signal())
}
#[cfg(not(target_family = "unix"))]
#[inline]
fn platform_exit_code(status: std::process::ExitStatus) -> Option<i32> {
status.code()
}
impl std::process::Termination for Code {
#[inline]
fn report(self) -> std::process::ExitCode {
self.as_exit_code()
.unwrap_or(std::process::ExitCode::FAILURE)
}
}