watchexec_events/
process.rs1use std::{
2 num::{NonZeroI32, NonZeroI64},
3 process::ExitStatus,
4};
5
6use watchexec_signals::Signal;
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[cfg_attr(feature = "serde", serde(tag = "disposition", content = "code"))]
25pub enum ProcessEnd {
26 #[cfg_attr(feature = "serde", serde(rename = "success"))]
28 Success,
29
30 #[cfg_attr(feature = "serde", serde(rename = "error"))]
32 ExitError(NonZeroI64),
33
34 #[cfg_attr(feature = "serde", serde(rename = "signal"))]
36 ExitSignal(Signal),
37
38 #[cfg_attr(feature = "serde", serde(rename = "stop"))]
40 ExitStop(NonZeroI32),
41
42 #[cfg_attr(feature = "serde", serde(rename = "exception"))]
44 Exception(NonZeroI32),
45
46 #[cfg_attr(feature = "serde", serde(rename = "continued"))]
48 Continued,
49}
50
51impl From<ExitStatus> for ProcessEnd {
52 #[cfg(unix)]
53 fn from(es: ExitStatus) -> Self {
54 use std::os::unix::process::ExitStatusExt;
55
56 match (es.code(), es.signal(), es.stopped_signal()) {
57 (Some(_), Some(_), _) => {
58 unreachable!("exitstatus cannot both be code and signal?!")
59 }
60 (Some(code), None, _) => {
61 NonZeroI64::try_from(i64::from(code)).map_or(Self::Success, Self::ExitError)
62 }
63 (None, Some(_), Some(stopsig)) => {
64 NonZeroI32::try_from(stopsig).map_or(Self::Success, Self::ExitStop)
65 }
66 #[cfg(not(target_os = "vxworks"))]
67 (None, Some(_), _) if es.continued() => Self::Continued,
68 (None, Some(signal), _) => Self::ExitSignal(signal.into()),
69 (None, None, _) => Self::Success,
70 }
71 }
72
73 #[cfg(windows)]
74 fn from(es: ExitStatus) -> Self {
75 match es.code().map(NonZeroI32::try_from) {
76 None | Some(Err(_)) => Self::Success,
77 Some(Ok(code)) if code.get() < 0 => Self::Exception(code),
78 Some(Ok(code)) => Self::ExitError(code.into()),
79 }
80 }
81
82 #[cfg(not(any(unix, windows)))]
83 fn from(es: ExitStatus) -> Self {
84 if es.success() {
85 Self::Success
86 } else {
87 Self::ExitError(NonZeroI64::new(1).unwrap())
88 }
89 }
90}
91
92impl ProcessEnd {
93 #[cfg(unix)]
100 #[must_use]
101 pub fn into_exitstatus(self) -> ExitStatus {
102 use std::os::unix::process::ExitStatusExt;
103 match self {
104 Self::Success => ExitStatus::from_raw(0),
105 Self::ExitError(code) => {
106 ExitStatus::from_raw(i32::from(u8::try_from(code.get()).unwrap_or_default()) << 8)
107 }
108 Self::ExitSignal(signal) => {
109 ExitStatus::from_raw(signal.to_nix().map_or(0, |sig| sig as i32))
110 }
111 Self::Continued => ExitStatus::from_raw(0xffff),
112 _ => unimplemented!(),
113 }
114 }
115
116 #[cfg(windows)]
121 #[must_use]
122 pub fn into_exitstatus(self) -> ExitStatus {
123 use std::os::windows::process::ExitStatusExt;
124 match self {
125 Self::Success => ExitStatus::from_raw(0),
126 Self::ExitError(code) => ExitStatus::from_raw(code.get().try_into().unwrap()),
127 _ => unimplemented!(),
128 }
129 }
130
131 #[cfg(not(any(unix, windows)))]
133 #[must_use]
134 pub fn into_exitstatus(self) -> ExitStatus {
135 unimplemented!()
136 }
137}