Skip to main content

win_error_codes/
hresult.rs

1// Copyright (c) Mike Grier.
2
3use std::fmt;
4
5use crate::Win32Error;
6
7#[cfg(test)]
8mod tests;
9
10/// The severity bit: set for a failure, which is also the sign bit, so a failure is negative.
11const SEVERITY_BIT: u32 = 0x8000_0000;
12/// The customer bit: set for a code a third party defined and not Microsoft.
13const CUSTOMER_BIT: u32 = 0x2000_0000;
14/// `FACILITY_NT_BIT`: set on an `HRESULT` made from an `NTSTATUS` by `HRESULT_FROM_NT`.
15pub(crate) const NTSTATUS_BIT: u32 = 0x1000_0000;
16/// Where the facility starts.
17const FACILITY_SHIFT: u32 = 16;
18/// `HRESULT_FACILITY`'s mask, 13 bits: wider than the 11 bits the documentation draws, because the
19/// macro takes the reserved bit and the `NTSTATUS` bit with it, and the DXGI facility, 0x87A, uses
20/// the first of them.
21const FACILITY_MASK: u32 = 0x1FFF;
22/// The code field, which is the low 16 bits.
23const CODE_MASK: u32 = 0x0000_FFFF;
24/// The severity and everything above the code, which is what a wrapped Win32 code fixes.
25const SEVERITY_AND_FACILITY: u32 = 0xFFFF_0000;
26/// `FACILITY_WIN32`.
27const FACILITY_WIN32: u32 = 7;
28/// What the high half of an `HRESULT` that wraps a Win32 code is: severity set, `FACILITY_WIN32`,
29/// and nothing else.
30const WIN32_WRAP: u32 = SEVERITY_BIT | (FACILITY_WIN32 << FACILITY_SHIFT);
31
32/// An `HRESULT`, the signed code COM and much of the Windows API report in.
33///
34/// An `i32`: `FAILED(hr)` is `hr < 0`, so the severity is the sign bit. The layout, from the top: the
35/// severity, a reserved bit, the customer bit, the `NTSTATUS` bit, another reserved bit, the facility,
36/// and a 16-bit code. It is a number space of its own, not [`Win32Error`]'s: a Win32 code is
37/// wrapped into one by [`from_win32`](Self::from_win32) and read back out by
38/// [`win32`](Self::win32), and neither direction is lossless in general (see each).
39///
40/// Success is any non-negative value, `S_OK` (zero) and `S_FALSE` (one) among them, which is why
41/// [`into_result`](Self::into_result) can not tell those apart and the raw value is there for a
42/// caller who must. It is not an `Error`, because a success is a value of this type.
43///
44/// # Example
45///
46/// ```
47/// use win_error_codes::{HResult, Win32Error};
48///
49/// // ERROR_IO_DEVICE, wrapped as HRESULT_FROM_WIN32 wraps it.
50/// let hresult = HResult::from_win32(Win32Error::from_code(1117));
51/// assert_eq!(hresult.raw().cast_unsigned(), 0x8007_045D);
52/// assert!(hresult.is_failure());
53/// assert_eq!(hresult.win32(), Some(Win32Error::from_code(1117)));
54/// assert_eq!(hresult.to_string(), "0x8007045D");
55///
56/// // E_FAIL is a failure that wraps no Win32 code.
57/// let e_fail = HResult::from_raw(0x8000_4005_u32.cast_signed());
58/// assert!(e_fail.is_failure());
59/// assert_eq!(e_fail.win32(), None);
60/// ```
61#[derive(Clone, Copy, PartialEq, Eq, Hash)]
62pub struct HResult(i32);
63
64impl HResult {
65    /// Wraps a raw `HRESULT`. Any value is accepted.
66    #[must_use]
67    pub const fn from_raw(raw: i32) -> Self {
68        Self(raw)
69    }
70
71    /// The raw `HRESULT`, unaltered.
72    #[must_use]
73    pub const fn raw(self) -> i32 {
74        self.0
75    }
76
77    /// `FAILED(hr)`: whether the severity bit is set, which is whether the value is negative.
78    #[must_use]
79    pub const fn is_failure(self) -> bool {
80        self.0 < 0
81    }
82
83    /// `SUCCEEDED(hr)`: any non-negative value, `S_OK` and `S_FALSE` among them.
84    #[must_use]
85    pub const fn is_success(self) -> bool {
86        !self.is_failure()
87    }
88
89    /// `Ok` for a success and `Err` carrying this value for a failure.
90    ///
91    /// # Errors
92    ///
93    /// This value, when it is a failure.
94    pub const fn into_result(self) -> Result<(), Self> {
95        if self.is_success() { Ok(()) } else { Err(self) }
96    }
97
98    /// `HRESULT_CODE`: the low 16 bits.
99    #[must_use]
100    pub const fn code(self) -> u16 {
101        (self.0.cast_unsigned() & CODE_MASK) as u16
102    }
103
104    /// `HRESULT_FACILITY`: the 13 bits above the code, as `winerror.h`'s macro takes them. That is
105    /// wider than the 11-bit facility the documentation draws; see the note on the mask.
106    #[must_use]
107    pub const fn facility(self) -> u16 {
108        ((self.0.cast_unsigned() >> FACILITY_SHIFT) & FACILITY_MASK) as u16
109    }
110
111    /// Whether the customer bit is set: a code a third party defined, not Microsoft.
112    #[must_use]
113    pub const fn is_customer(self) -> bool {
114        self.0.cast_unsigned() & CUSTOMER_BIT != 0
115    }
116
117    /// Whether the `NTSTATUS` bit is set, which is what `HRESULT_FROM_NT` does to a status.
118    #[must_use]
119    pub const fn is_ntstatus_mapped(self) -> bool {
120        self.0.cast_unsigned() & NTSTATUS_BIT != 0
121    }
122
123    /// `HRESULT_FROM_WIN32`: wraps a Win32 code with the severity bit and `FACILITY_WIN32`.
124    ///
125    /// **Not lossless, and that is the macro's own rule.** Zero is `S_OK`. A code that is not
126    /// positive as an `i32`, so one of 2^31 or more, is taken to be an `HRESULT` already and is
127    /// returned as it is. Any other code keeps only its low 16 bits: `0x0010045D` and `0x0000045D`
128    /// both give `0x8007045D`. [`win32`](Self::win32) is the inverse only for the codes from 1 to
129    /// 65535.
130    #[must_use]
131    pub const fn from_win32(error: Win32Error) -> Self {
132        let code = error.code();
133        if code.cast_signed() <= 0 {
134            Self(code.cast_signed())
135        } else {
136            Self(((code & CODE_MASK) | WIN32_WRAP).cast_signed())
137        }
138    }
139
140    /// The Win32 code this wraps, or `None` if it is not one `from_win32` makes: severity set,
141    /// `FACILITY_WIN32`, and no other bit above the code.
142    ///
143    /// `None` for `S_OK`, though `from_win32` of zero is `S_OK`: a success wraps no error, which is
144    /// the reading durable-ioring's `ErrorCode` already takes and tests. So the round trip holds for
145    /// the codes from 1 to 65535 and not for zero or anything above.
146    #[must_use]
147    pub const fn win32(self) -> Option<Win32Error> {
148        let bits = self.0.cast_unsigned();
149        if bits & SEVERITY_AND_FACILITY == WIN32_WRAP {
150            Some(Win32Error::from_code(bits & CODE_MASK))
151        } else {
152            None
153        }
154    }
155}
156
157/// The value in hex, as `winerror.h` writes it: `0x8007045D`.
158impl fmt::Display for HResult {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        write!(f, "{:#010X}", self.0.cast_unsigned())
161    }
162}
163
164/// `HResult(0x8007045D)`, in hex and not as the negative number the `i32` is.
165impl fmt::Debug for HResult {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        write!(f, "HResult({self})")
168    }
169}