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}