1use super::*;
2
3/// An error object consists of both an error code as well as detailed error information for debugging.
4#[derive(Clone, PartialEq, Eq)]
5pub struct Error {
6 pub(crate) code: HRESULT,
7 pub(crate) info: Option<crate::imp::IRestrictedErrorInfo>,
8}
9
10impl Error {
11 /// An error object without any failure information.
12 pub const OK: Self = Self { code: HRESULT(0), info: None };
13
14 /// This creates a new WinRT error object, capturing the stack and other information about the
15 /// point of failure.
16 pub fn new(code: HRESULT, message: HSTRING) -> Self {
17 unsafe {
18 if let Some(function) = crate::imp::delay_load::<RoOriginateError>(s!("combase.dll"), s!("RoOriginateError")) {
19 function(code, std::mem::transmute_copy(&message));
20 }
21 let info = GetErrorInfo().and_then(|e| e.cast()).ok();
22 Self { code, info }
23 }
24 }
25
26 pub fn from_win32() -> Self {
27 unsafe { Self { code: HRESULT::from_win32(crate::imp::GetLastError()), info: None } }
28 }
29
30 /// The error code describing the error.
31 pub const fn code(&self) -> HRESULT {
32 self.code
33 }
34
35 /// The error information describing the error.
36 pub const fn info(&self) -> &Option<crate::imp::IRestrictedErrorInfo> {
37 &self.info
38 }
39
40 /// The error message describing the error.
41 pub fn message(&self) -> HSTRING {
42 // First attempt to retrieve the restricted error information.
43 if let Some(info) = &self.info {
44 let mut fallback = BSTR::default();
45 let mut message = BSTR::default();
46 let mut code = HRESULT(0);
47
48 unsafe {
49 let _ = info.GetErrorDetails(&mut fallback, &mut code, &mut message, &mut BSTR::default());
50 }
51
52 if self.code == code {
53 let message = if !message.is_empty() { message } else { fallback };
54 return HSTRING::from_wide(crate::imp::wide_trim_end(message.as_wide())).unwrap_or_default();
55 }
56 }
57
58 self.code.message()
59 }
60}
61
62impl std::convert::From<Error> for HRESULT {
63 fn from(error: Error) -> Self {
64 let code: HRESULT = error.code;
65 let info: Option<crate::imp::IErrorInfo> = error.info.and_then(|info: IRestrictedErrorInfo| info.cast().ok());
66
67 unsafe {
68 let _ = crate::imp::SetErrorInfo(dwreserved:0, perrinfo:info.as_ref());
69 }
70
71 code
72 }
73}
74
75impl std::convert::From<Error> for std::io::Error {
76 fn from(from: Error) -> Self {
77 Self::from_raw_os_error(code:from.code.0)
78 }
79}
80
81impl std::convert::From<std::string::FromUtf16Error> for Error {
82 fn from(_: std::string::FromUtf16Error) -> Self {
83 Self { code: HRESULT::from_win32(crate::imp::ERROR_NO_UNICODE_TRANSLATION), info: None }
84 }
85}
86
87impl std::convert::From<std::string::FromUtf8Error> for Error {
88 fn from(_: std::string::FromUtf8Error) -> Self {
89 Self { code: HRESULT::from_win32(crate::imp::ERROR_NO_UNICODE_TRANSLATION), info: None }
90 }
91}
92
93impl std::convert::From<std::num::TryFromIntError> for Error {
94 fn from(_: std::num::TryFromIntError) -> Self {
95 Self { code: HRESULT(crate::imp::E_INVALIDARG), info: None }
96 }
97}
98
99// Unfortunately this is needed to make types line up. The Rust type system does
100// not know the `Infallible` can never be constructed. This code needs to be here
101// to satesify the type checker but it will never be run. Once `!` is stabilizied
102// this can be removed.
103impl std::convert::From<std::convert::Infallible> for Error {
104 fn from(_: std::convert::Infallible) -> Self {
105 unreachable!()
106 }
107}
108
109impl std::convert::From<HRESULT> for Error {
110 fn from(code: HRESULT) -> Self {
111 let info: Option<crate::imp::IRestrictedErrorInfo> = GetErrorInfo().and_then(|e| e.cast()).ok();
112
113 if let Some(info) = info {
114 // If it does (and therefore running on a recent version of Windows)
115 // then capture_propagation_context adds a breadcrumb to the error
116 // info to make debugging easier.
117 if let Ok(capture) = info.cast::<crate::imp::ILanguageExceptionErrorInfo2>() {
118 unsafe {
119 let _ = capture.CapturePropagationContext(None);
120 }
121 }
122
123 return Self { code, info: Some(info) };
124 }
125
126 if let Ok(info) = GetErrorInfo() {
127 let message = unsafe { info.GetDescription().unwrap_or_default() };
128 Self::new(code, HSTRING::from_wide(message.as_wide()).unwrap_or_default())
129 } else {
130 Self { code, info: None }
131 }
132 }
133}
134
135impl std::fmt::Debug for Error {
136 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 let mut debug: DebugStruct<'_, '_> = fmt.debug_struct(name:"Error");
138 debug.field("code", &self.code).field(name:"message", &self.message()).finish()
139 }
140}
141
142impl std::fmt::Display for Error {
143 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144 let message: HSTRING = self.message();
145 if message.is_empty() {
146 std::write!(fmt, "{}", self.code())
147 } else {
148 std::write!(fmt, "{} ({})", self.message(), self.code())
149 }
150 }
151}
152
153impl std::error::Error for Error {}
154
155type RoOriginateError = extern "system" fn(code: HRESULT, message: *mut std::ffi::c_void) -> i32;
156
157fn GetErrorInfo() -> Result<crate::imp::IErrorInfo> {
158 unsafe { crate::imp::GetErrorInfo(dwreserved:0) }
159}
160