watchexec_events/
process.rsuse std::{
num::{NonZeroI32, NonZeroI64},
process::ExitStatus,
};
use watchexec_signals::Signal;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "disposition", content = "code"))]
pub enum ProcessEnd {
#[cfg_attr(feature = "serde", serde(rename = "success"))]
Success,
#[cfg_attr(feature = "serde", serde(rename = "error"))]
ExitError(NonZeroI64),
#[cfg_attr(feature = "serde", serde(rename = "signal"))]
ExitSignal(Signal),
#[cfg_attr(feature = "serde", serde(rename = "stop"))]
ExitStop(NonZeroI32),
#[cfg_attr(feature = "serde", serde(rename = "exception"))]
Exception(NonZeroI32),
#[cfg_attr(feature = "serde", serde(rename = "continued"))]
Continued,
}
impl From<ExitStatus> for ProcessEnd {
#[cfg(unix)]
fn from(es: ExitStatus) -> Self {
use std::os::unix::process::ExitStatusExt;
match (es.code(), es.signal(), es.stopped_signal()) {
(Some(_), Some(_), _) => {
unreachable!("exitstatus cannot both be code and signal?!")
}
(Some(code), None, _) => {
NonZeroI64::try_from(i64::from(code)).map_or(Self::Success, Self::ExitError)
}
(None, Some(_), Some(stopsig)) => {
NonZeroI32::try_from(stopsig).map_or(Self::Success, Self::ExitStop)
}
#[cfg(not(target_os = "vxworks"))]
(None, Some(_), _) if es.continued() => Self::Continued,
(None, Some(signal), _) => Self::ExitSignal(signal.into()),
(None, None, _) => Self::Success,
}
}
#[cfg(windows)]
fn from(es: ExitStatus) -> Self {
match es.code().map(NonZeroI32::try_from) {
None | Some(Err(_)) => Self::Success,
Some(Ok(code)) if code.get() < 0 => Self::Exception(code),
Some(Ok(code)) => Self::ExitError(code.into()),
}
}
#[cfg(not(any(unix, windows)))]
fn from(es: ExitStatus) -> Self {
if es.success() {
Self::Success
} else {
Self::ExitError(NonZeroI64::new(1).unwrap())
}
}
}
impl ProcessEnd {
#[cfg(unix)]
#[must_use]
pub fn into_exitstatus(self) -> ExitStatus {
use std::os::unix::process::ExitStatusExt;
match self {
Self::Success => ExitStatus::from_raw(0),
Self::ExitError(code) => {
ExitStatus::from_raw(i32::from(u8::try_from(code.get()).unwrap_or_default()) << 8)
}
Self::ExitSignal(signal) => {
ExitStatus::from_raw(signal.to_nix().map_or(0, |sig| sig as i32))
}
Self::Continued => ExitStatus::from_raw(0xffff),
_ => unimplemented!(),
}
}
#[cfg(windows)]
#[must_use]
pub fn into_exitstatus(self) -> ExitStatus {
use std::os::windows::process::ExitStatusExt;
match self {
Self::Success => ExitStatus::from_raw(0),
Self::ExitError(code) => ExitStatus::from_raw(code.get().try_into().unwrap()),
_ => unimplemented!(),
}
}
#[cfg(not(any(unix, windows)))]
#[must_use]
pub fn into_exitstatus(self) -> ExitStatus {
unimplemented!()
}
}