1use crate::raw::RawExitCode;
7use core::fmt::Display;
8
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
11#[cfg_attr(
12 feature = "serde",
13 derive(serde::Serialize, serde::Deserialize),
14 serde(transparent)
15)]
16pub struct ExitCode(u32);
17
18impl ExitCode {
19 pub const SUCCESS: Self = Self(0);
25
26 pub const GENERAL_ERROR: Self = Self(1);
32
33 pub const FILE_NOT_FOUND: Self = Self(2);
37
38 pub const PATH_NOT_FOUND: Self = Self(3);
42
43 pub const ACCESS_DENIED: Self = Self(5);
47
48 pub const NOT_ENOUGH_MEMORY: Self = Self(8);
52
53 pub const INVALID_PARAMETER: Self = Self(87);
57
58 pub const BROKEN_PIPE: Self = Self(109);
62
63 pub const COMMAND_NOT_RECOGNIZED: Self = Self(9009);
69
70 pub const TERMINATED_BY_CTRL_C: Self = Self(0xC000_013A);
74
75 pub const ACCESS_VIOLATION: Self = Self(0xC000_0005);
79
80 pub const STACK_OVERFLOW: Self = Self(0xC000_00FD);
84}
85
86impl RawExitCode for ExitCode {
87 type Code = u32;
88
89 fn from_raw(code: Self::Code) -> Self {
90 Self(code)
91 }
92
93 fn to_raw(&self) -> Self::Code {
94 self.0
95 }
96}
97
98impl From<u32> for ExitCode {
99 fn from(code: u32) -> Self {
100 ExitCode::from_raw(code)
101 }
102}
103
104impl Display for ExitCode {
105 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
106 self.0.fmt(f)
107 }
108}
109
110#[cfg(all(windows, feature = "std"))]
111impl From<std::process::ExitStatus> for ExitCode {
112 fn from(status: std::process::ExitStatus) -> ExitCode {
113 ExitCode::from_raw(
114 status
115 .code()
116 .expect("cannot fail on Windows")
117 .try_into()
118 .unwrap(),
119 )
120 }
121}
122
123#[cfg(all(windows, feature = "std"))]
124impl From<ExitCode> for std::process::ExitStatus {
125 fn from(code: ExitCode) -> Self {
126 use std::os::windows::process::ExitStatusExt;
127 std::process::ExitStatus::from_raw(code.to_raw())
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn test_from_raw() {
137 assert_eq!(ExitCode::from_raw(0).to_raw(), 0);
138 }
139
140 #[test]
141 fn test_is_success() {
142 assert!(ExitCode::SUCCESS.is_success());
143 }
144
145 #[test]
146 fn test_is_failure() {
147 assert!(ExitCode::GENERAL_ERROR.is_failure());
148 }
149
150 #[test]
151 fn test_from_u32() {
152 let code: ExitCode = 1.into();
153 assert_eq!(code.to_raw(), 1);
154 }
155
156 #[test]
157 #[cfg(all(feature = "std", windows))]
158 fn test_from_exit_status() {
159 use std::os::windows::process::ExitStatusExt;
160 use std::process::ExitStatus;
161
162 let success_status = ExitStatus::from_raw(0);
164 let success_code: ExitCode = success_status.into();
165 assert!(success_code.is_success());
166 assert_eq!(success_code.to_raw(), 0);
167
168 let failure_status = ExitStatus::from_raw(1);
170 let failure_code: ExitCode = failure_status.into();
171 assert!(failure_code.is_failure());
172 assert_eq!(failure_code.to_raw(), 1);
173 }
174}
175
176#[cfg(all(test, windows, feature = "serde"))]
177mod serde_tests {
178 use super::*;
179 use serde_json;
180
181 #[test]
182 fn test_serde() {
183 let code = ExitCode::SUCCESS;
184 let serialized = serde_json::to_string(&code).unwrap();
185 let deserialized: ExitCode = serde_json::from_str(&serialized).unwrap();
186 assert_eq!(code, deserialized);
187 }
188}