proc_result/unix/
exit_code.rs

1use core::fmt::Display;
2
3use crate::raw::RawExitCode;
4
5/// A Unix-like exit code.
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7#[cfg_attr(
8    feature = "serde",
9    derive(serde::Serialize, serde::Deserialize),
10    serde(transparent)
11)]
12pub struct ExitCode(u8);
13
14impl ExitCode {
15    /// The program terminated successfully.
16    ///
17    /// Corresponds to exit code `0`.
18    ///
19    /// This is the universal success code.
20    pub const SUCCESS: Self = Self(0);
21
22    /// The program terminated with a general, unspecified error.
23    ///
24    /// Corresponds to exit code `1`.
25    ///
26    /// This is a common "catch-all" for general failures.
27    pub const GENERAL_ERROR: Self = Self(1);
28
29    /// The command line arguments were invalid or used incorrectly.
30    ///
31    /// Corresponds to exit code `2`.
32    ///
33    /// Often used by shell builtins (e.g., `bash`'s `exit 2` for `builtin_status_bad_usage`).
34    pub const INVALID_ARGS: Self = Self(2);
35
36    /// The command was used incorrectly.
37    ///
38    /// Corresponds to exit code `64` (`EX_USAGE` from `sysexits.h`).
39    pub const USAGE: Self = Self(64);
40
41    /// The program received a malformed or invalid input.
42    ///
43    /// Corresponds to exit code `65` (`EX_DATAERR` from `sysexits.h`).
44    pub const DATA_ERROR: Self = Self(65);
45
46    /// An input file (not a system file) did not exist or was not readable.
47    ///
48    /// Corresponds to exit code `66` (`EX_NOINPUT` from `sysexits.h`).
49    pub const NO_INPUT: Self = Self(66);
50
51    /// The user specified did not exist.
52    ///
53    /// Corresponds to exit code `67` (`EX_NOUSER` from `sysexits.h`).
54    pub const NO_USER: Self = Self(67);
55
56    /// The host specified did not exist.
57    ///
58    /// Corresponds to exit code `68` (`EX_NOHOST` from `sysexits.h`).
59    pub const NO_HOST: Self = Self(68);
60
61    /// A server is unavailable.
62    ///
63    /// Corresponds to exit code `69` (`EX_UNAVAILABLE` from `sysexits.h`).
64    ///
65    /// Often used when something does not work as expected.
66    pub const UNAVAILABLE: Self = Self(69);
67
68    /// An internal software error occurred.
69    ///
70    /// Corresponds to exit code `70` (`EX_SOFTWARE` from `sysexits.h`).
71    ///
72    /// Should be limited to non-operating system software errors.
73    pub const SOFTWARE: Self = Self(70);
74
75    /// An operating system error occurred.
76    ///
77    /// Corresponds to exit code `71` (`EX_OSERR` from `sysexits.h`).
78    ///
79    /// Intended to be used for errors such as "cannot fork" or "cannot create pipe".
80    pub const OS_ERROR: Self = Self(71);
81
82    /// A system file did not exist, cannot be opened, or has an incorrect format.
83    ///
84    /// Corresponds to exit code `72` (`EX_OSFILE` from `sysexits.h`).
85    pub const OS_FILE: Self = Self(72);
86
87    /// A (user specified) output file cannot be created.
88    ///
89    /// Corresponds to exit code `73` (`EX_CANTCREAT` from `sysexits.h`).
90    pub const CANT_CREATE: Self = Self(73);
91
92    /// An error occurred while reading or writing to a file.
93    ///
94    /// Corresponds to exit code `74` (`EX_IOERR` from `sysexits.h`).
95    pub const IO_ERROR: Self = Self(74);
96
97    /// A temporary failure occurred.
98    ///
99    /// Corresponds to exit code `75` (`EX_TEMPFAIL` from `sysexits.h`).
100    ///
101    /// The request may be retried later.
102    pub const TEMP_FAIL: Self = Self(75);
103
104    /// A remote system returned something that was "not possible" during a protocol exchange.
105    ///
106    /// Corresponds to exit code `76` (`EX_PROTOCOL` from `sysexits.h`).
107    pub const PROTOCOL: Self = Self(76);
108
109    /// The user specified did not have sufficient permissions to perform the operation.
110    ///
111    /// Corresponds to exit code `77` (`EX_NOPERM` from `sysexits.h`).
112    ///
113    /// Not intended for file system problems, (use [`ExitCode::NO_INPUT`] or
114    /// [`ExitCode::CANT_CREATE`]) but for higher-level permissions.
115    pub const NO_PERM: Self = Self(77);
116
117    /// Something was found in an unconfigured or misconfigured state.
118    ///
119    /// Corresponds to exit code `78` (`EX_CONFIG` from `sysexits.h`).
120    pub const CONFIG: Self = Self(78);
121
122    /// The command was found but could not be executed (e.g. permission denied).
123    ///
124    /// Corresponds to exit code `126`.
125    ///
126    /// Often returned by shells when a command is found but not executable.
127    pub const COMMAND_CANNOT_EXECUTE: Self = Self(126);
128
129    /// The command or program could not be found.
130    ///
131    /// Corresponds to exit code `127`.
132    ///
133    /// Often returned by shells when a command is not found in `PATH`.
134    pub const COMMAND_NOT_FOUND: Self = Self(127);
135
136    /// Creates a new `UnixExitCode` from the underlying `u8` code.
137    #[must_use]
138    pub const fn from_raw(code: u8) -> Self {
139        Self(code)
140    }
141
142    /// Returns the underlying `u8` code.
143    #[must_use]
144    pub const fn to_raw(&self) -> u8 {
145        self.0
146    }
147
148    /// Returns `true` if the exit code represents a successful termination.
149    #[must_use]
150    pub const fn is_success(&self) -> bool {
151        self.0 == Self::SUCCESS.to_raw()
152    }
153
154    /// Returns `true` if the exit code represents a failure termination.
155    #[must_use]
156    pub const fn is_failure(&self) -> bool {
157        !self.is_success()
158    }
159}
160
161impl Display for ExitCode {
162    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
163        self.0.fmt(f)
164    }
165}
166
167impl RawExitCode for ExitCode {
168    type Code = u8;
169
170    fn from_raw(code: Self::Code) -> Self {
171        ExitCode::from_raw(code)
172    }
173
174    fn to_raw(&self) -> Self::Code {
175        self.to_raw()
176    }
177}
178
179impl From<u8> for ExitCode {
180    fn from(code: u8) -> Self {
181        ExitCode::from_raw(code)
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn test_from_raw() {
191        assert_eq!(ExitCode::from_raw(0).to_raw(), 0);
192    }
193
194    #[test]
195    fn test_is_success() {
196        assert!(ExitCode::SUCCESS.is_success());
197    }
198
199    #[test]
200    fn test_is_failure() {
201        assert!(ExitCode::GENERAL_ERROR.is_failure());
202    }
203
204    #[test]
205    fn test_from_u8() {
206        let code: ExitCode = 1.into();
207        assert_eq!(code.to_raw(), 1);
208    }
209}
210
211#[cfg(all(test, feature = "serde"))]
212mod serde_tests {
213    use super::*;
214    use serde_json;
215
216    #[test]
217    fn test_serde() {
218        let code = ExitCode::GENERAL_ERROR;
219        let serialized = serde_json::to_string(&code).unwrap();
220        let deserialized: ExitCode = serde_json::from_str(&serialized).unwrap();
221        assert_eq!(code, deserialized);
222    }
223}