use super::{ExitCode, Signal};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WaitState {
Exited {
exit_code: ExitCode,
},
Signaled {
signal: Signal,
core_dump: bool,
},
Unsupported(i32),
}
impl WaitState {
#[must_use]
pub const fn from_raw(status: i32) -> Self {
if Self::is_w_exited(status) {
Self::Exited {
exit_code: ExitCode::from_raw(Self::w_exit_status(status)),
}
} else if Self::is_w_signaled(status) {
Self::Signaled {
signal: Signal::from_raw(Self::w_term_sig(status)),
core_dump: Self::is_w_coredump(status),
}
} else {
Self::Unsupported(status)
}
}
#[must_use]
pub const fn to_raw(&self) -> i32 {
match self {
Self::Exited { exit_code } => (exit_code.to_raw() as i32) << 8,
Self::Signaled { signal, core_dump } => {
(signal.to_raw() as i32) | if *core_dump { 0x80 } else { 0 }
}
Self::Unsupported(code) => *code,
}
}
const _WSTOPPED: i32 = 0x7F;
#[allow(non_snake_case)]
#[inline]
#[must_use]
const fn _WSTATUS(status: i32) -> i32 {
status & 0xFF
}
#[allow(non_snake_case)]
#[inline]
#[must_use]
const fn WIFSIGNALED(status: i32) -> bool {
Self::_WSTATUS(status) != Self::_WSTOPPED && Self::_WSTATUS(status) != 0
}
#[allow(non_snake_case)]
#[inline]
#[must_use]
const fn WTERMSIG(status: i32) -> i32 {
status & 0x7F
}
#[allow(non_snake_case, clippy::verbose_bit_mask)]
#[inline]
#[must_use]
const fn WIFEXITED(status: i32) -> bool {
(status & 0o177) == 0
}
#[allow(non_snake_case)]
#[inline]
#[must_use]
const fn WEXITSTATUS(status: i32) -> i32 {
(status >> 8) & 0xFF
}
#[allow(non_snake_case)]
#[inline]
#[must_use]
const fn WCOREDUMP(status: i32) -> bool {
(status & 0o200) != 0
}
#[must_use]
pub const fn is_w_exited(status: i32) -> bool {
Self::WIFEXITED(status)
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
#[must_use]
pub const fn w_exit_status(status: i32) -> u8 {
Self::WEXITSTATUS(status) as u8
}
#[must_use]
pub const fn is_w_signaled(status: i32) -> bool {
Self::WIFSIGNALED(status)
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
#[must_use]
pub const fn w_term_sig(status: i32) -> u8 {
Self::WTERMSIG(status) as u8
}
#[must_use]
pub const fn is_w_coredump(status: i32) -> bool {
Self::WCOREDUMP(status)
}
}
impl From<i32> for WaitState {
fn from(status: i32) -> Self {
Self::from_raw(status)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_raw_exited_0() {
let status = WaitState::from_raw(0x0000_0000);
assert_eq!(
status,
WaitState::Exited {
exit_code: ExitCode::from_raw(0),
}
);
}
#[test]
fn test_to_raw_exited_0() {
let status = WaitState::Exited {
exit_code: ExitCode::from_raw(0),
};
assert_eq!(status.to_raw(), 0x0000_0000);
}
#[test]
fn test_from_raw_exited_1() {
let status = WaitState::from_raw(0x0000_0100);
assert_eq!(
status,
WaitState::Exited {
exit_code: ExitCode::from_raw(1),
}
);
}
#[test]
fn test_to_raw_exited_1() {
let status = WaitState::Exited {
exit_code: ExitCode::from_raw(1),
};
assert_eq!(status.to_raw(), 0x0000_0100);
}
#[test]
fn test_from_raw_signaled() {
let status = WaitState::from_raw(0x0000_0001);
assert_eq!(
status,
WaitState::Signaled {
signal: Signal::from_raw(1),
core_dump: false,
}
);
}
#[test]
fn test_to_raw_signaled() {
let status = WaitState::Signaled {
signal: Signal::from_raw(1),
core_dump: false,
};
assert_eq!(status.to_raw(), 0x0000_0001);
}
#[test]
fn test_from_raw_signaled_with_coredump() {
let status = WaitState::from_raw(0x0000_0081);
assert_eq!(
status,
WaitState::Signaled {
signal: Signal::from_raw(1),
core_dump: true,
}
);
}
#[test]
fn test_to_raw_signaled_with_coredump() {
let status = WaitState::Signaled {
signal: Signal::from_raw(1),
core_dump: true,
};
assert_eq!(status.to_raw(), 0x0000_0081);
}
}
#[cfg(all(test, unix))]
mod libc_verification_tests {
use super::*;
use libc::{WCOREDUMP, WEXITSTATUS, WIFEXITED, WIFSIGNALED, WTERMSIG};
#[test]
fn test_wifexited_true() {
assert!(WIFEXITED(0x0000_0000));
assert!(WaitState::is_w_exited(0x0000_0000));
}
#[test]
fn test_wifexited_false() {
assert!(!WIFEXITED(0x0000_0001));
assert!(!WaitState::is_w_exited(0x0000_0001));
}
#[test]
fn test_wexitstatus_success() {
assert_eq!(WEXITSTATUS(0x0000_0000), 0);
assert_eq!(WaitState::w_exit_status(0x0000_0000), 0);
}
#[test]
fn test_wexitstatus_failure() {
assert_eq!(WEXITSTATUS(0x0000_0100), 1);
assert_eq!(WaitState::w_exit_status(0x0000_0100), 1);
}
#[test]
fn test_wifsignaled_true() {
assert!(WIFSIGNALED(0x0000_0001));
assert!(WaitState::is_w_signaled(0x0000_0001));
}
#[test]
fn test_wifsignaled_false() {
assert!(!WIFSIGNALED(0x0000_0000));
assert!(!WaitState::is_w_signaled(0x0000_0000));
}
#[test]
fn test_wtermsig() {
assert_eq!(WTERMSIG(0x0000_0001), 1);
assert_eq!(WaitState::w_term_sig(0x0000_0001), 1);
}
#[test]
fn test_wcoredump_true() {
assert!(WCOREDUMP(0x0000_0081));
assert!(WaitState::is_w_coredump(0x0000_0081));
}
#[test]
fn test_wcoredump_false() {
assert!(!WCOREDUMP(0x0000_0001));
assert!(!WaitState::is_w_coredump(0x0000_0001));
}
}