1use super::*;
2use core::num::NonZeroI32;
3
4#[expect(unused_imports)]
5use core::mem::size_of;
6
7#[derive(Clone)]
29pub struct Error {
30 code: NonZeroI32,
35
36 info: ErrorInfo,
38}
39
40const S_EMPTY_ERROR: NonZeroI32 = const_nonzero_i32(u32::from_be_bytes(*b"S_OK") as i32);
43
44const fn const_nonzero_i32(i: i32) -> NonZeroI32 {
48 if let Some(nz) = NonZeroI32::new(i) {
49 nz
50 } else {
51 panic!();
52 }
53}
54
55fn nonzero_hresult(hr: HRESULT) -> NonZeroI32 {
56 if let Some(nz) = NonZeroI32::new(hr.0) {
57 nz
58 } else {
59 S_EMPTY_ERROR
60 }
61}
62
63impl Error {
64 pub const fn empty() -> Self {
66 Self {
67 code: S_EMPTY_ERROR,
68 info: ErrorInfo::empty(),
69 }
70 }
71
72 pub fn new<T: AsRef<str>>(code: HRESULT, message: T) -> Self {
75 let message: &str = message.as_ref();
76 if message.is_empty() {
77 Self::from_hresult(code)
78 } else {
79 ErrorInfo::originate_error(code, message);
80 code.into()
81 }
82 }
83
84 pub fn from_hresult(code: HRESULT) -> Self {
86 Self {
87 code: nonzero_hresult(code),
88 info: ErrorInfo::empty(),
89 }
90 }
91
92 pub fn from_thread() -> Self {
94 Self::from_hresult(HRESULT::from_thread())
95 }
96
97 pub const fn code(&self) -> HRESULT {
99 if self.code.get() == S_EMPTY_ERROR.get() {
100 HRESULT(0)
101 } else {
102 HRESULT(self.code.get())
103 }
104 }
105
106 pub fn message(&self) -> String {
108 if let Some(message) = self.info.message() {
109 return message;
110 }
111
112 self.code().message()
114 }
115
116 #[cfg(windows)]
118 pub fn as_ptr(&self) -> *mut core::ffi::c_void {
119 self.info.as_ptr()
120 }
121}
122
123#[cfg(feature = "std")]
124impl std::error::Error for Error {}
125
126impl From<Error> for HRESULT {
127 fn from(error: Error) -> Self {
128 let code = error.code();
129 error.info.into_thread();
130 code
131 }
132}
133
134impl From<HRESULT> for Error {
135 fn from(code: HRESULT) -> Self {
136 Self {
137 code: nonzero_hresult(code),
138 info: ErrorInfo::from_thread(),
139 }
140 }
141}
142
143#[cfg(feature = "std")]
144impl From<Error> for std::io::Error {
145 fn from(from: Error) -> Self {
146 if let Some(win32) = WIN32_ERROR::from_error(&from) {
154 Self::from_raw_os_error(win32.0 as i32)
155 } else {
156 Self::from_raw_os_error(from.code().0)
157 }
158 }
159}
160
161#[cfg(feature = "std")]
162impl From<std::io::Error> for Error {
163 fn from(from: std::io::Error) -> Self {
164 match from.raw_os_error() {
165 Some(status) => WIN32_ERROR(status as u32).into(),
166 None => HRESULT(E_UNEXPECTED).into(),
167 }
168 }
169}
170
171impl From<alloc::string::FromUtf16Error> for Error {
172 fn from(_: alloc::string::FromUtf16Error) -> Self {
173 WIN32_ERROR(ERROR_NO_UNICODE_TRANSLATION as u32).into()
174 }
175}
176
177impl From<alloc::string::FromUtf8Error> for Error {
178 fn from(_: alloc::string::FromUtf8Error) -> Self {
179 WIN32_ERROR(ERROR_NO_UNICODE_TRANSLATION as u32).into()
180 }
181}
182
183impl From<core::num::TryFromIntError> for Error {
184 fn from(_: core::num::TryFromIntError) -> Self {
185 WIN32_ERROR(ERROR_INVALID_DATA as u32).into()
186 }
187}
188
189impl core::fmt::Debug for Error {
190 fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
191 let mut debug = fmt.debug_struct("Error");
192 debug
193 .field("code", &self.code())
194 .field("message", &self.message())
195 .finish()
196 }
197}
198
199impl core::fmt::Display for Error {
200 fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
201 let message = self.message();
202 if message.is_empty() {
203 core::write!(fmt, "{}", self.code())
204 } else {
205 core::write!(fmt, "{} ({})", message, self.code())
206 }
207 }
208}
209
210impl core::hash::Hash for Error {
211 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
212 self.code.hash(state);
213 }
215}
216
217impl PartialEq for Error {
219 fn eq(&self, other: &Self) -> bool {
220 self.code == other.code
221 }
222}
223
224impl Eq for Error {}
225
226impl PartialOrd for Error {
227 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
228 Some(self.cmp(other))
229 }
230}
231
232impl Ord for Error {
233 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
234 self.code.cmp(&other.code)
235 }
236}
237
238use error_info::*;
239
240#[cfg(all(windows, not(windows_slim_errors)))]
241mod error_info {
242 use super::*;
243 use crate::com::ComPtr;
244
245 #[derive(Clone, Default)]
253 pub(crate) struct ErrorInfo {
254 pub(super) ptr: Option<ComPtr>,
255 }
256
257 impl ErrorInfo {
258 pub(crate) const fn empty() -> Self {
259 Self { ptr: None }
260 }
261
262 pub(crate) fn from_thread() -> Self {
263 let mut ptr: *mut core::ffi::c_void = core::ptr::null_mut();
264 unsafe { GetErrorInfo(0, &mut ptr) };
265 Self {
266 ptr: core::ptr::NonNull::new(ptr).map(ComPtr),
267 }
268 }
269
270 pub(crate) fn into_thread(self) {
271 if let Some(ptr) = self.ptr {
272 unsafe {
273 SetErrorInfo(0, ptr.as_raw());
274 }
275 }
276 }
277
278 pub(crate) fn originate_error(code: HRESULT, message: &str) {
279 let message: Vec<_> = message.encode_utf16().collect();
280 unsafe {
281 RoOriginateErrorW(code.0, message.len() as u32, message.as_ptr());
282 }
283 }
284
285 pub(crate) fn message(&self) -> Option<String> {
286 use crate::bstr::BasicString;
287
288 let ptr = self.ptr.as_ref()?;
289
290 let mut message = BasicString::default();
291
292 if let Some(info) = ptr.cast(&IID_IRestrictedErrorInfo) {
294 let mut fallback = BasicString::default();
295 let mut code = 0;
296
297 unsafe {
298 com_call!(
299 IRestrictedErrorInfo_Vtbl,
300 info.GetErrorDetails(
301 &mut fallback as *mut _ as _,
302 &mut code,
303 &mut message as *mut _ as _,
304 &mut BasicString::default() as *mut _ as _
305 )
306 );
307 }
308
309 if message.is_empty() {
310 message = fallback;
311 };
312 }
313
314 if message.is_empty() {
316 unsafe {
317 com_call!(
318 IErrorInfo_Vtbl,
319 ptr.GetDescription(&mut message as *mut _ as _)
320 );
321 }
322 }
323
324 Some(String::from_utf16_lossy(wide_trim_end(&message)))
325 }
326
327 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
328 if let Some(info) = self.ptr.as_ref() {
329 info.as_raw()
330 } else {
331 core::ptr::null_mut()
332 }
333 }
334 }
335
336 unsafe impl Send for ErrorInfo {}
337 unsafe impl Sync for ErrorInfo {}
338}
339
340#[cfg(not(all(windows, not(windows_slim_errors))))]
341mod error_info {
342 use super::*;
343
344 #[derive(Clone, Default)]
347 pub(crate) struct EmptyErrorInfo;
348
349 pub(crate) use self::EmptyErrorInfo as ErrorInfo;
350
351 impl EmptyErrorInfo {
352 pub(crate) const fn empty() -> Self {
353 Self
354 }
355
356 pub(crate) fn from_thread() -> Self {
357 Self
358 }
359
360 pub(crate) fn into_thread(self) {}
361
362 pub(crate) fn originate_error(_code: HRESULT, _message: &str) {}
363
364 pub(crate) fn message(&self) -> Option<String> {
365 None
366 }
367
368 #[cfg(windows)]
369 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
370 core::ptr::null_mut()
371 }
372 }
373}