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