proc_result/unix/
wait_state.rs1use super::{ExitCode, Signal};
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum WaitState {
11 Exited {
13 exit_code: ExitCode,
15 },
16
17 Signaled {
19 signal: Signal,
21
22 core_dump: bool,
24 },
25
26 Unsupported(i32),
28}
29
30impl WaitState {
31 #[must_use]
33 pub const fn from_raw(status: i32) -> Self {
34 if Self::is_w_exited(status) {
35 Self::Exited {
36 exit_code: ExitCode::from_raw(Self::w_exit_status(status)),
37 }
38 } else if Self::is_w_signaled(status) {
39 Self::Signaled {
40 signal: Signal::from_raw(Self::w_term_sig(status)),
41 core_dump: Self::is_w_coredump(status),
42 }
43 } else {
44 Self::Unsupported(status)
45 }
46 }
47
48 #[must_use]
57 pub const fn to_raw(&self) -> i32 {
58 match self {
59 Self::Exited { exit_code } => (exit_code.to_raw() as i32) << 8,
60 Self::Signaled { signal, core_dump } => {
61 (signal.to_raw() as i32) | if *core_dump { 0x80 } else { 0 }
62 }
63 Self::Unsupported(code) => *code,
64 }
65 }
66
67 const _WSTOPPED: i32 = 0x7F;
69
70 #[allow(non_snake_case)]
72 #[inline]
73 #[must_use]
74 const fn _WSTATUS(status: i32) -> i32 {
75 status & 0xFF
76 }
77
78 #[allow(non_snake_case)]
80 #[inline]
81 #[must_use]
82 const fn WIFSIGNALED(status: i32) -> bool {
83 Self::_WSTATUS(status) != Self::_WSTOPPED && Self::_WSTATUS(status) != 0
84 }
85
86 #[allow(non_snake_case)]
88 #[inline]
89 #[must_use]
90 const fn WTERMSIG(status: i32) -> i32 {
91 status & 0x7F
92 }
93
94 #[allow(non_snake_case, clippy::verbose_bit_mask)]
96 #[inline]
97 #[must_use]
98 const fn WIFEXITED(status: i32) -> bool {
99 (status & 0o177) == 0
100 }
101
102 #[allow(non_snake_case)]
104 #[inline]
105 #[must_use]
106 const fn WEXITSTATUS(status: i32) -> i32 {
107 (status >> 8) & 0xFF
108 }
109
110 #[allow(non_snake_case)]
112 #[inline]
113 #[must_use]
114 const fn WCOREDUMP(status: i32) -> bool {
115 (status & 0o200) != 0
116 }
117
118 #[must_use]
122 pub const fn is_w_exited(status: i32) -> bool {
123 Self::WIFEXITED(status)
124 }
125
126 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
134 #[must_use]
135 pub const fn w_exit_status(status: i32) -> u8 {
136 Self::WEXITSTATUS(status) as u8
137 }
138
139 #[must_use]
143 pub const fn is_w_signaled(status: i32) -> bool {
144 Self::WIFSIGNALED(status)
145 }
146
147 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
155 #[must_use]
156 pub const fn w_term_sig(status: i32) -> u8 {
157 Self::WTERMSIG(status) as u8
158 }
159
160 #[must_use]
169 pub const fn is_w_coredump(status: i32) -> bool {
170 Self::WCOREDUMP(status)
171 }
172}
173
174impl From<i32> for WaitState {
175 fn from(status: i32) -> Self {
176 Self::from_raw(status)
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn test_from_raw_exited_0() {
186 let status = WaitState::from_raw(0x0000_0000);
187 assert_eq!(
188 status,
189 WaitState::Exited {
190 exit_code: ExitCode::from_raw(0),
191 }
192 );
193 }
194
195 #[test]
196 fn test_to_raw_exited_0() {
197 let status = WaitState::Exited {
198 exit_code: ExitCode::from_raw(0),
199 };
200 assert_eq!(status.to_raw(), 0x0000_0000);
201 }
202
203 #[test]
204 fn test_from_raw_exited_1() {
205 let status = WaitState::from_raw(0x0000_0100);
206 assert_eq!(
207 status,
208 WaitState::Exited {
209 exit_code: ExitCode::from_raw(1),
210 }
211 );
212 }
213
214 #[test]
215 fn test_to_raw_exited_1() {
216 let status = WaitState::Exited {
217 exit_code: ExitCode::from_raw(1),
218 };
219 assert_eq!(status.to_raw(), 0x0000_0100);
220 }
221
222 #[test]
223 fn test_from_raw_signaled() {
224 let status = WaitState::from_raw(0x0000_0001);
225 assert_eq!(
226 status,
227 WaitState::Signaled {
228 signal: Signal::from_raw(1),
229 core_dump: false,
230 }
231 );
232 }
233
234 #[test]
235 fn test_to_raw_signaled() {
236 let status = WaitState::Signaled {
237 signal: Signal::from_raw(1),
238 core_dump: false,
239 };
240 assert_eq!(status.to_raw(), 0x0000_0001);
241 }
242
243 #[test]
244 fn test_from_raw_signaled_with_coredump() {
245 let status = WaitState::from_raw(0x0000_0081);
246 assert_eq!(
247 status,
248 WaitState::Signaled {
249 signal: Signal::from_raw(1),
250 core_dump: true,
251 }
252 );
253 }
254
255 #[test]
256 fn test_to_raw_signaled_with_coredump() {
257 let status = WaitState::Signaled {
258 signal: Signal::from_raw(1),
259 core_dump: true,
260 };
261 assert_eq!(status.to_raw(), 0x0000_0081);
262 }
263}
264
265#[cfg(all(test, unix))]
267mod libc_verification_tests {
268 use super::*;
269 use libc::{WCOREDUMP, WEXITSTATUS, WIFEXITED, WIFSIGNALED, WTERMSIG};
270
271 #[test]
272 fn test_wifexited_true() {
273 assert!(WIFEXITED(0x0000_0000));
274 assert!(WaitState::is_w_exited(0x0000_0000));
275 }
276
277 #[test]
278 fn test_wifexited_false() {
279 assert!(!WIFEXITED(0x0000_0001));
280 assert!(!WaitState::is_w_exited(0x0000_0001));
281 }
282
283 #[test]
284 fn test_wexitstatus_success() {
285 assert_eq!(WEXITSTATUS(0x0000_0000), 0);
286 assert_eq!(WaitState::w_exit_status(0x0000_0000), 0);
287 }
288
289 #[test]
290 fn test_wexitstatus_failure() {
291 assert_eq!(WEXITSTATUS(0x0000_0100), 1);
292 assert_eq!(WaitState::w_exit_status(0x0000_0100), 1);
293 }
294
295 #[test]
296 fn test_wifsignaled_true() {
297 assert!(WIFSIGNALED(0x0000_0001));
298 assert!(WaitState::is_w_signaled(0x0000_0001));
299 }
300
301 #[test]
302 fn test_wifsignaled_false() {
303 assert!(!WIFSIGNALED(0x0000_0000));
304 assert!(!WaitState::is_w_signaled(0x0000_0000));
305 }
306
307 #[test]
308 fn test_wtermsig() {
309 assert_eq!(WTERMSIG(0x0000_0001), 1);
310 assert_eq!(WaitState::w_term_sig(0x0000_0001), 1);
311 }
312
313 #[test]
314 fn test_wcoredump_true() {
315 assert!(WCOREDUMP(0x0000_0081));
316 assert!(WaitState::is_w_coredump(0x0000_0081));
317 }
318
319 #[test]
320 fn test_wcoredump_false() {
321 assert!(!WCOREDUMP(0x0000_0001));
322 assert!(!WaitState::is_w_coredump(0x0000_0001));
323 }
324}