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}