proc_result/
lib.rs

1//! A tiny cross-platforrm library containing exit status and code types.
2//!
3//! Unlike `std::process`, this crate does not require the standard library[^1], nor
4//! `libc`, and can create and interpret exit codes of non-current platforms. For
5//! example, on Windows, it can read and interpret exit codes that may have been
6//! recorded from a Linux process, or vice versa.
7//!
8//! [^1]: The `std` feature is enabled by default, but can be disabled.
9#![cfg_attr(not(feature = "std"), no_std)]
10
11use core::fmt::Display;
12
13use raw::RawExitCode;
14
15pub mod raw;
16pub mod unix;
17pub mod windows;
18
19// Import README.md so that doc tests run on it.
20#[allow(dead_code)]
21mod doc_tests {
22    #[doc = include_str!("../README.md")]
23    struct Readme;
24}
25
26/// An exit code or exit state returned by a program.
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28#[non_exhaustive]
29pub enum ProcResult {
30    /// An unclassified exit status on a Unix platform.
31    Unix(unix::WaitStatus),
32
33    /// An unclassified exit status on a Windows platform.
34    Windows(windows::ExitCode),
35}
36
37impl ProcResult {
38    /// Creates a new `ProcResult` that represents a successful termination.
39    #[cfg(all(feature = "std", unix))]
40    #[must_use]
41    pub fn default_success() -> Self {
42        Self::Unix(unix::WaitStatus::default())
43    }
44
45    /// Creates a new `ProcResult` that represents a successful termination.
46    #[cfg(all(feature = "std", windows))]
47    #[must_use]
48    pub fn default_success() -> Self {
49        Self::Windows(windows::ExitCode::default())
50    }
51
52    /// Creates a new `ProcResult` that represents a non-zero exit code.
53    #[cfg(all(feature = "std", unix))]
54    #[must_use]
55    pub fn default_failure() -> Self {
56        Self::Unix(unix::WaitStatus::from_raw(1)) // Non-zero exit code
57    }
58
59    /// Creates a new `ProcResult` that represents a non-zero exit code.
60    #[cfg(all(feature = "std", windows))]
61    #[must_use]
62    pub fn default_failure() -> Self {
63        Self::Windows(windows::ExitCode::from_raw(1)) // Non-zero exit code
64    }
65
66    /// Returns a result that is `Ok` if the exit code or status indicates a success.
67    ///
68    /// # Errors
69    ///
70    /// Returns `Self` if not [`ProcResult::is_success`].
71    pub fn ok(&self) -> Result<(), Self> {
72        if self.is_success() {
73            Ok(())
74        } else {
75            Err(*self)
76        }
77    }
78
79    /// Returns whether the process terminated successfully.
80    #[must_use]
81    pub fn is_success(&self) -> bool {
82        match self {
83            ProcResult::Unix(status) => status.exit_code().is_some_and(|code| code.is_success()),
84            ProcResult::Windows(code) => code.is_success(),
85        }
86    }
87
88    /// Returns whether the process did not terminate successfully.
89    #[must_use]
90    pub fn is_failure(&self) -> bool {
91        !self.is_success()
92    }
93}
94
95#[cfg(feature = "std")]
96impl Display for ProcResult {
97    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
98        match self {
99            Self::Unix(status) => write!(f, "Unix exit status: {}", status.to_raw()),
100            Self::Windows(code) => write!(f, "Windows exit code: {}", code.to_raw()),
101        }
102    }
103}
104
105impl core::error::Error for ProcResult {}
106
107#[cfg(all(feature = "std", unix))]
108impl From<std::process::ExitStatus> for ProcResult {
109    #[allow(unreachable_code)]
110    fn from(status: std::process::ExitStatus) -> Self {
111        Self::Unix(status.into())
112    }
113}
114
115#[cfg(all(feature = "std", windows))]
116impl From<std::process::ExitStatus> for ProcResult {
117    #[allow(unreachable_code)]
118    fn from(status: std::process::ExitStatus) -> Self {
119        Self::Windows(status.into())
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    #[test]
126    #[cfg(feature = "std")]
127    fn test_default_success_proc_result() {
128        use super::ProcResult;
129
130        let result = ProcResult::default_success();
131        assert!(result.is_success());
132    }
133
134    #[test]
135    #[cfg(feature = "std")]
136    fn test_default_failure_proc_result() {
137        use super::ProcResult;
138
139        let result = ProcResult::default_failure();
140        assert!(result.is_failure());
141    }
142}