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    /// Returns a result that is `Ok` if the exit code or status indicates a success.
39    ///
40    /// # Errors
41    ///
42    /// Returns `Self` if not [`ProcResult::is_success`].
43    pub fn ok(&self) -> Result<(), Self> {
44        if self.is_success() {
45            Ok(())
46        } else {
47            Err(*self)
48        }
49    }
50
51    /// Returns whether the process terminated successfully.
52    #[must_use]
53    pub fn is_success(&self) -> bool {
54        match self {
55            ProcResult::Unix(status) => status.exit_code().is_some_and(|code| code.is_success()),
56            ProcResult::Windows(code) => code.is_success(),
57        }
58    }
59
60    /// Returns whether the process did not terminate successfully.
61    #[must_use]
62    pub fn is_failure(&self) -> bool {
63        !self.is_success()
64    }
65}
66
67#[cfg(all(feature = "std", unix))]
68impl Default for ProcResult {
69    fn default() -> Self {
70        Self::Unix(unix::WaitStatus::default())
71    }
72}
73
74#[cfg(all(feature = "std", windows))]
75impl Default for ProcResult {
76    fn default() -> Self {
77        Self::Windows(windows::ExitCode::default())
78    }
79}
80
81#[cfg(feature = "std")]
82impl Display for ProcResult {
83    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
84        match self {
85            Self::Unix(status) => write!(f, "Unix exit status: {}", status.to_raw()),
86            Self::Windows(code) => write!(f, "Windows exit code: {}", code.to_raw()),
87        }
88    }
89}
90
91impl core::error::Error for ProcResult {}
92
93#[cfg(all(feature = "std", unix))]
94impl From<std::process::ExitStatus> for ProcResult {
95    #[allow(unreachable_code)]
96    fn from(status: std::process::ExitStatus) -> Self {
97        Self::Unix(status.into())
98    }
99}
100
101#[cfg(all(feature = "std", windows))]
102impl From<std::process::ExitStatus> for ProcResult {
103    #[allow(unreachable_code)]
104    fn from(status: std::process::ExitStatus) -> Self {
105        Self::Windows(status.into())
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    #[test]
112    #[cfg(feature = "std")]
113    fn test_default_proc_result() {
114        use super::ProcResult;
115
116        let result = ProcResult::default();
117        assert!(
118            result.is_success(),
119            "Default ProcResult should indicate success"
120        );
121    }
122}