Skip to main content

win_error_codes/
win32_error.rs

1// Copyright (c) Mike Grier.
2
3use std::fmt;
4#[cfg(windows)]
5use std::io;
6
7#[cfg(test)]
8mod tests;
9
10/// `ERROR_SUCCESS`: the code that means the call succeeded. A Win32 error code of zero is not
11/// the absence of a code.
12const ERROR_SUCCESS: u32 = 0;
13
14/// A Win32 error code, exactly as Windows produced it.
15///
16/// The `DWORD` that `GetLastError` returns, and that a call such as `GetStorageDependencyInformation`
17/// returns directly. Deliberately not an enum: the type carries whatever Windows said, including a
18/// code this crate has never heard of. **It is not assumed to fit in 16 bits**: the virtdisk errors
19/// are `WIN32_ERROR`s above 2^31, and `u32` holds them.
20///
21/// `ERROR_SUCCESS` is an ordinary value of this type, zero, and [`is_success`](Self::is_success)
22/// and [`into_result`](Self::into_result) are how a call's return is turned into a `Result`.
23///
24/// # Example
25///
26/// ```
27/// use win_error_codes::Win32Error;
28///
29/// // ERROR_FILE_NOT_FOUND.
30/// let error = Win32Error::from_code(2);
31/// assert_eq!(error.code(), 2);
32/// assert!(!error.is_success());
33///
34/// // A call that returns its code directly: zero is success.
35/// assert_eq!(Win32Error::from_code(0).into_result(), Ok(()));
36/// assert_eq!(Win32Error::from_code(2).into_result(), Err(error));
37///
38/// // A code above 2^31 is carried as it is.
39/// assert_eq!(Win32Error::from_code(0xC03A_0015).code(), 0xC03A_0015);
40/// ```
41///
42/// On Windows the `io::Error` form keeps the code exactly, however large it is:
43///
44/// ```
45/// # #[cfg(windows)] {
46/// use win_error_codes::Win32Error;
47///
48/// // ERROR_VIRTDISK_NOT_VIRTUAL_DISK.
49/// let error = Win32Error::from_code(0xC03A_0015);
50/// let io_error = error.to_io_error();
51///
52/// assert_eq!(Win32Error::from_io(&io_error), Some(error));
53/// # }
54/// ```
55#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
56pub struct Win32Error(u32);
57
58impl Win32Error {
59    /// Wraps a raw code. Any value is accepted, zero included.
60    #[must_use]
61    pub const fn from_code(code: u32) -> Self {
62        Self(code)
63    }
64
65    /// The raw code, unaltered.
66    #[must_use]
67    pub const fn code(self) -> u32 {
68        self.0
69    }
70
71    /// Whether this is `ERROR_SUCCESS`, which is zero.
72    #[must_use]
73    pub const fn is_success(self) -> bool {
74        self.0 == ERROR_SUCCESS
75    }
76
77    /// `Ok` for `ERROR_SUCCESS` and `Err` carrying this code for anything else, which is how a call
78    /// that returns its code directly becomes a `Result`.
79    ///
80    /// # Errors
81    ///
82    /// This code, when it is not `ERROR_SUCCESS`.
83    pub const fn into_result(self) -> Result<(), Self> {
84        if self.is_success() { Ok(()) } else { Err(self) }
85    }
86}
87
88#[cfg(windows)]
89impl Win32Error {
90    /// The code in an [`io::Error`], or `None` for one that carries no OS error at all (one made
91    /// with `io::Error::other`, or from an [`io::ErrorKind`] alone).
92    ///
93    /// `None` and not zero: zero is `ERROR_SUCCESS`, so an error with no code that came back as a
94    /// code would read as a success.
95    ///
96    /// Std's `io::Error` holds an OS error as an `i32`, so the cast back to the `u32` a Win32 code
97    /// is happens here and is bit-preserving: a code above 2^31, which std holds as a negative
98    /// number, comes back as it went in.
99    #[must_use]
100    pub fn from_io(error: &io::Error) -> Option<Self> {
101        error.raw_os_error().map(|raw| Self(raw.cast_unsigned()))
102    }
103
104    /// The same failure as a standard [`io::Error`], for callers that funnel everything through
105    /// `std::io`.
106    ///
107    /// A re-presentation and not a reclassification: [`io::Error::raw_os_error`] on the result is
108    /// this code, cast at std's `i32` boundary and bit-preserved, **for every code**, and
109    /// [`from_io`](Self::from_io) reverses it exactly.
110    #[must_use]
111    pub fn to_io_error(self) -> io::Error {
112        io::Error::from_raw_os_error(self.0.cast_signed())
113    }
114
115    /// The calling thread's last error, as `GetLastError` holds it.
116    ///
117    /// Call this immediately after the failing call: the slot is volatile thread state, and almost
118    /// any other Win32 call overwrites it, a `CloseHandle` in a `Drop` included.
119    #[must_use]
120    pub fn last() -> Self {
121        // `last_os_error` is always an OS error, so the zero is never taken.
122        Self(
123            io::Error::last_os_error()
124                .raw_os_error()
125                .map_or(ERROR_SUCCESS, i32::cast_unsigned),
126        )
127    }
128}
129
130#[cfg(windows)]
131impl From<Win32Error> for io::Error {
132    fn from(error: Win32Error) -> Self {
133        error.to_io_error()
134    }
135}
136
137/// The code in decimal and in hex, and on Windows the system's message for it after a colon.
138///
139/// The message is [`io::Error`]'s own rendering, which this crate does not edit: it ends with std's
140/// `(os error N)`, where `N` is the code as std holds it, so a code above 2^31 appears there as a
141/// negative number as well, and a code the system has no message for says so.
142impl fmt::Display for Win32Error {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        write!(f, "Win32 error {} (0x{:08X})", self.0, self.0)?;
145        #[cfg(windows)]
146        write!(f, ": {}", self.to_io_error())?;
147        Ok(())
148    }
149}
150
151impl std::error::Error for Win32Error {}