windows_result/
hresult.rs

1use super::*;
2
3/// An error code value returned by most COM functions.
4#[repr(transparent)]
5#[derive(Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
6#[must_use]
7#[allow(non_camel_case_types)]
8pub struct HRESULT(pub i32);
9
10impl HRESULT {
11    /// Returns [`true`] if `self` is a success code.
12    #[inline]
13    pub const fn is_ok(self) -> bool {
14        self.0 >= 0
15    }
16
17    /// Returns [`true`] if `self` is a failure code.
18    #[inline]
19    pub const fn is_err(self) -> bool {
20        !self.is_ok()
21    }
22
23    /// Asserts that `self` is a success code.
24    ///
25    /// This will invoke the [`panic!`] macro if `self` is a failure code and display
26    /// the [`HRESULT`] value for diagnostics.
27    #[inline]
28    #[track_caller]
29    pub fn unwrap(self) {
30        assert!(self.is_ok(), "HRESULT 0x{:X}", self.0);
31    }
32
33    /// Converts the [`HRESULT`] to [`Result<()>`][Result<_>].
34    #[inline]
35    pub fn ok(self) -> Result<()> {
36        if self.is_ok() {
37            Ok(())
38        } else {
39            Err(self.into())
40        }
41    }
42
43    /// Calls `op` if `self` is a success code, otherwise returns [`HRESULT`]
44    /// converted to [`Result<T>`].
45    #[inline]
46    pub fn map<F, T>(self, op: F) -> Result<T>
47    where
48        F: FnOnce() -> T,
49    {
50        self.ok()?;
51        Ok(op())
52    }
53
54    /// Calls `op` if `self` is a success code, otherwise returns [`HRESULT`]
55    /// converted to [`Result<T>`].
56    #[inline]
57    pub fn and_then<F, T>(self, op: F) -> Result<T>
58    where
59        F: FnOnce() -> Result<T>,
60    {
61        self.ok()?;
62        op()
63    }
64
65    /// The error message describing the error.
66    pub fn message(self) -> String {
67        #[cfg(windows)]
68        {
69            let mut message = HeapString::default();
70            let mut code = self.0;
71            let mut module = core::ptr::null_mut();
72
73            let mut flags = FORMAT_MESSAGE_ALLOCATE_BUFFER
74                | FORMAT_MESSAGE_FROM_SYSTEM
75                | FORMAT_MESSAGE_IGNORE_INSERTS;
76
77            unsafe {
78                if self.0 & 0x1000_0000 == 0x1000_0000 {
79                    code ^= 0x1000_0000;
80                    flags |= FORMAT_MESSAGE_FROM_HMODULE;
81
82                    module = LoadLibraryExA(
83                        b"ntdll.dll\0".as_ptr(),
84                        core::ptr::null_mut(),
85                        LOAD_LIBRARY_SEARCH_DEFAULT_DIRS,
86                    );
87                }
88
89                let size = FormatMessageW(
90                    flags,
91                    module as _,
92                    code as _,
93                    0,
94                    &mut message.0 as *mut _ as *mut _,
95                    0,
96                    core::ptr::null(),
97                );
98
99                if !message.0.is_null() && size > 0 {
100                    String::from_utf16_lossy(wide_trim_end(core::slice::from_raw_parts(
101                        message.0,
102                        size as usize,
103                    )))
104                } else {
105                    String::default()
106                }
107            }
108        }
109
110        #[cfg(not(windows))]
111        {
112            return alloc::format!("0x{:08x}", self.0 as u32);
113        }
114    }
115
116    /// Maps a Win32 error code to an HRESULT value.
117    pub const fn from_win32(error: u32) -> Self {
118        Self(if error as i32 <= 0 {
119            error
120        } else {
121            (error & 0x0000_FFFF) | (7 << 16) | 0x8000_0000
122        } as i32)
123    }
124
125    /// Maps an NT error code to an HRESULT value.
126    pub const fn from_nt(error: i32) -> Self {
127        Self(if error >= 0 {
128            error
129        } else {
130            error | 0x1000_0000
131        })
132    }
133}
134
135impl<T> From<Result<T>> for HRESULT {
136    fn from(result: Result<T>) -> Self {
137        if let Err(error) = result {
138            return error.into();
139        }
140        HRESULT(0)
141    }
142}
143
144impl core::fmt::Display for HRESULT {
145    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
146        f.write_fmt(format_args!("{:#010X}", self.0))
147    }
148}
149
150impl core::fmt::Debug for HRESULT {
151    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
152        f.write_fmt(format_args!("HRESULT({})", self))
153    }
154}