proc_result/
windows.rs

1//! Windows-specific exit codes and handling.
2//!
3//! This module is cross-platform, but on Windows, it provides conversions to/from
4//! [`std::process::ExitStatus`].
5
6use crate::raw::RawExitCode;
7use core::fmt::Display;
8
9/// A Windows-specific exit code.
10#[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    /// The program terminated successfully.
20    ///
21    /// Corresponds to exit code `0`.
22    ///
23    /// This is the universal success code.
24    pub const SUCCESS: Self = Self(0);
25
26    /// The program terminated with a general, unspecified error.
27    ///
28    /// Corresponds to exit code `1`.
29    ///
30    /// This is a common "catch-all" for general failures.
31    pub const GENERAL_ERROR: Self = Self(1);
32
33    /// The system cannot find the file specified.
34    ///
35    /// Corresponds to exit code `2` (`ERROR_FILE_NOT_FOUND`).
36    pub const FILE_NOT_FOUND: Self = Self(2);
37
38    /// The system cannot find the path specified.
39    ///
40    /// Corresponds to exit code `3` (`ERROR_PATH_NOT_FOUND`).
41    pub const PATH_NOT_FOUND: Self = Self(3);
42
43    /// Access is denied.
44    ///
45    /// Corresponds to exit code `5` (`ERROR_ACCESS_DENIED`).
46    pub const ACCESS_DENIED: Self = Self(5);
47
48    /// Not enough storage is available to process this command.
49    ///
50    /// Corresponds to exit code `8` (`ERROR_NOT_ENOUGH_MEMORY`).
51    pub const NOT_ENOUGH_MEMORY: Self = Self(8);
52
53    /// The parameter is incorrect.
54    ///
55    /// Corresponds to exit code `87` (`ERROR_INVALID_PARAMETER`).
56    pub const INVALID_PARAMETER: Self = Self(87);
57
58    /// The pipe has been ended.
59    ///
60    /// Corresponds to exit code `109` (`ERROR_BROKEN_PIPE`).
61    pub const BROKEN_PIPE: Self = Self(109);
62
63    /// The program is not recognized as a command, operable program, or batch file.
64    ///
65    /// Corresponds to exit code `9009`.
66    ///
67    /// This is a common code returned by `cmd.exe` when a command cannot be found or executed.
68    pub const COMMAND_NOT_RECOGNIZED: Self = Self(9009);
69
70    /// The program terminated as a result of a CTRL+C or Ctrl+Break signal.
71    ///
72    /// Cooresponds to exit code `0xC000_013A`.
73    pub const TERMINATED_BY_CTRL_C: Self = Self(0xC000_013A);
74
75    /// The program was terminate due to an access violation.
76    ///
77    /// Corresponds to exit code `0xC000_0005`.
78    pub const ACCESS_VIOLATION: Self = Self(0xC000_0005);
79
80    /// The program terminated due to a stack oveflow.
81    ///
82    /// Corresponds to exit code `0xC000_00FD`.
83    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        // Simulate a successful exit status
163        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        // Simulate a failure exit status
169        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}