use super::*; /// An error code value returned by most COM functions. #[repr(transparent)] #[derive(Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] #[must_use] #[allow(non_camel_case_types)] pub struct HRESULT(pub i32); impl HRESULT { /// Returns [`true`] if `self` is a success code. #[inline] pub const fn is_ok(self) -> bool { self.0 >= 0 } /// Returns [`true`] if `self` is a failure code. #[inline] pub const fn is_err(self) -> bool { !self.is_ok() } /// Asserts that `self` is a success code. /// /// This will invoke the [`panic!`] macro if `self` is a failure code and display /// the [`HRESULT`] value for diagnostics. #[inline] #[track_caller] pub fn unwrap(self) { assert!(self.is_ok(), "HRESULT 0x{:X}", self.0); } /// Converts the [`HRESULT`] to [`Result<()>`][Result<_>]. #[inline] pub fn ok(self) -> Result<()> { if self.is_ok() { Ok(()) } else { Err(self.into()) } } /// Calls `op` if `self` is a success code, otherwise returns [`HRESULT`] /// converted to [`Result`]. #[inline] pub fn map(self, op: F) -> Result where F: FnOnce() -> T, { self.ok()?; Ok(op()) } /// Calls `op` if `self` is a success code, otherwise returns [`HRESULT`] /// converted to [`Result`]. #[inline] pub fn and_then(self, op: F) -> Result where F: FnOnce() -> Result, { self.ok()?; op() } /// The error message describing the error. pub fn message(self) -> String { #[cfg(windows)] { let mut message = HeapString::default(); let mut code = self.0; let mut module = core::ptr::null_mut(); let mut flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; unsafe { if self.0 & 0x1000_0000 == 0x1000_0000 { code ^= 0x1000_0000; flags |= FORMAT_MESSAGE_FROM_HMODULE; module = LoadLibraryExA( b"ntdll.dll\0".as_ptr(), core::ptr::null_mut(), LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, ); } let size = FormatMessageW( flags, module as _, code as _, 0, &mut message.0 as *mut _ as *mut _, 0, core::ptr::null(), ); if !message.0.is_null() && size > 0 { String::from_utf16_lossy(wide_trim_end(core::slice::from_raw_parts( message.0, size as usize, ))) } else { String::default() } } } #[cfg(not(windows))] { return alloc::format!("0x{:08x}", self.0 as u32); } } /// Maps a Win32 error code to an HRESULT value. pub const fn from_win32(error: u32) -> Self { Self(if error as i32 <= 0 { error } else { (error & 0x0000_FFFF) | (7 << 16) | 0x8000_0000 } as i32) } /// Maps an NT error code to an HRESULT value. pub const fn from_nt(error: i32) -> Self { Self(if error >= 0 { error } else { error | 0x1000_0000 }) } } impl From> for HRESULT { fn from(result: Result) -> Self { if let Err(error) = result { return error.into(); } HRESULT(0) } } impl core::fmt::Display for HRESULT { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_fmt(format_args!("{:#010X}", self.0)) } } impl core::fmt::Debug for HRESULT { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_fmt(format_args!("HRESULT({self})")) } }