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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
use core::fmt::Display;
use crate::raw::RawExitCode;
/// A Unix-like exit code.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(transparent)
)]
pub struct ExitCode(u8);
impl ExitCode {
/// The program terminated successfully.
///
/// Corresponds to exit code `0`.
///
/// This is the universal success code.
pub const SUCCESS: Self = Self(0);
/// The program terminated with a general, unspecified error.
///
/// Corresponds to exit code `1`.
///
/// This is a common "catch-all" for general failures.
pub const GENERAL_ERROR: Self = Self(1);
/// The command line arguments were invalid or used incorrectly.
///
/// Corresponds to exit code `2`.
///
/// Often used by shell builtins (e.g., `bash`'s `exit 2` for `builtin_status_bad_usage`).
pub const INVALID_ARGS: Self = Self(2);
/// The command was used incorrectly.
///
/// Corresponds to exit code `64` (`EX_USAGE` from `sysexits.h`).
pub const USAGE: Self = Self(64);
/// The program received a malformed or invalid input.
///
/// Corresponds to exit code `65` (`EX_DATAERR` from `sysexits.h`).
pub const DATA_ERROR: Self = Self(65);
/// An input file (not a system file) did not exist or was not readable.
///
/// Corresponds to exit code `66` (`EX_NOINPUT` from `sysexits.h`).
pub const NO_INPUT: Self = Self(66);
/// The user specified did not exist.
///
/// Corresponds to exit code `67` (`EX_NOUSER` from `sysexits.h`).
pub const NO_USER: Self = Self(67);
/// The host specified did not exist.
///
/// Corresponds to exit code `68` (`EX_NOHOST` from `sysexits.h`).
pub const NO_HOST: Self = Self(68);
/// A server is unavailable.
///
/// Corresponds to exit code `69` (`EX_UNAVAILABLE` from `sysexits.h`).
///
/// Often used when something does not work as expected.
pub const UNAVAILABLE: Self = Self(69);
/// An internal software error occurred.
///
/// Corresponds to exit code `70` (`EX_SOFTWARE` from `sysexits.h`).
///
/// Should be limited to non-operating system software errors.
pub const SOFTWARE: Self = Self(70);
/// An operating system error occurred.
///
/// Corresponds to exit code `71` (`EX_OSERR` from `sysexits.h`).
///
/// Intended to be used for errors such as "cannot fork" or "cannot create pipe".
pub const OS_ERROR: Self = Self(71);
/// A system file did not exist, cannot be opened, or has an incorrect format.
///
/// Corresponds to exit code `72` (`EX_OSFILE` from `sysexits.h`).
pub const OS_FILE: Self = Self(72);
/// A (user specified) output file cannot be created.
///
/// Corresponds to exit code `73` (`EX_CANTCREAT` from `sysexits.h`).
pub const CANT_CREATE: Self = Self(73);
/// An error occurred while reading or writing to a file.
///
/// Corresponds to exit code `74` (`EX_IOERR` from `sysexits.h`).
pub const IO_ERROR: Self = Self(74);
/// A temporary failure occurred.
///
/// Corresponds to exit code `75` (`EX_TEMPFAIL` from `sysexits.h`).
///
/// The request may be retried later.
pub const TEMP_FAIL: Self = Self(75);
/// A remote system returned something that was "not possible" during a protocol exchange.
///
/// Corresponds to exit code `76` (`EX_PROTOCOL` from `sysexits.h`).
pub const PROTOCOL: Self = Self(76);
/// The user specified did not have sufficient permissions to perform the operation.
///
/// Corresponds to exit code `77` (`EX_NOPERM` from `sysexits.h`).
///
/// Not intended for file system problems, (use [`ExitCode::NO_INPUT`] or
/// [`ExitCode::CANT_CREATE`]) but for higher-level permissions.
pub const NO_PERM: Self = Self(77);
/// Something was found in an unconfigured or misconfigured state.
///
/// Corresponds to exit code `78` (`EX_CONFIG` from `sysexits.h`).
pub const CONFIG: Self = Self(78);
/// The command was found but could not be executed (e.g. permission denied).
///
/// Corresponds to exit code `126`.
///
/// Often returned by shells when a command is found but not executable.
pub const COMMAND_CANNOT_EXECUTE: Self = Self(126);
/// The command or program could not be found.
///
/// Corresponds to exit code `127`.
///
/// Often returned by shells when a command is not found in `PATH`.
pub const COMMAND_NOT_FOUND: Self = Self(127);
/// Creates a new `UnixExitCode` from the underlying `u8` code.
#[must_use]
pub const fn from_raw(code: u8) -> Self {
Self(code)
}
/// Returns the underlying `u8` code.
#[must_use]
pub const fn to_raw(&self) -> u8 {
self.0
}
/// Returns `true` if the exit code represents a successful termination.
#[must_use]
pub const fn is_success(&self) -> bool {
self.0 == Self::SUCCESS.to_raw()
}
/// Returns `true` if the exit code represents a failure termination.
#[must_use]
pub const fn is_failure(&self) -> bool {
!self.is_success()
}
}
impl Display for ExitCode {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.fmt(f)
}
}
impl RawExitCode for ExitCode {
type Code = u8;
fn from_raw(code: Self::Code) -> Self {
ExitCode::from_raw(code)
}
fn to_raw(&self) -> Self::Code {
self.to_raw()
}
}
impl From<u8> for ExitCode {
fn from(code: u8) -> Self {
ExitCode::from_raw(code)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_raw() {
assert_eq!(ExitCode::from_raw(0).to_raw(), 0);
}
#[test]
fn test_is_success() {
assert!(ExitCode::SUCCESS.is_success());
}
#[test]
fn test_is_failure() {
assert!(ExitCode::GENERAL_ERROR.is_failure());
}
#[test]
fn test_from_u8() {
let code: ExitCode = 1.into();
assert_eq!(code.to_raw(), 1);
}
}
#[cfg(all(test, feature = "serde"))]
mod serde_tests {
use super::*;
use serde_json;
#[test]
fn test_serde() {
let code = ExitCode::GENERAL_ERROR;
let serialized = serde_json::to_string(&code).unwrap();
let deserialized: ExitCode = serde_json::from_str(&serialized).unwrap();
assert_eq!(code, deserialized);
}
}