1#![cfg(windows)]
2#![cfg_attr(not(feature = "std"), no_std)]
3#![warn(
4 unsafe_op_in_unsafe_fn,
5 missing_docs,
6 missing_debug_implementations,
7 missing_copy_implementations,
8 rust_2018_idioms,
9 clippy::todo,
10 clippy::manual_assert,
11 clippy::must_use_candidate,
12 clippy::inconsistent_struct_constructor,
13 clippy::wrong_self_convention,
14 clippy::missing_const_for_fn,
15 rustdoc::broken_intra_doc_links,
16 rustdoc::private_intra_doc_links
17)]
18
19use core::{
51 fmt::{self, Display, Write},
52 mem::MaybeUninit,
53 ptr,
54};
55
56#[cfg(feature = "std")]
57use std::{error::Error, io};
58
59use winapi::{
60 shared::minwindef::DWORD,
61 um::{
62 errhandlingapi::GetLastError,
63 winbase::{
64 FormatMessageW, FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS,
65 FORMAT_MESSAGE_MAX_WIDTH_MASK,
66 },
67 },
68};
69
70#[repr(transparent)]
73#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
74pub struct Win32Error(DWORD);
75
76impl Win32Error {
77 #[must_use]
79 pub const fn new(code: DWORD) -> Self {
80 Self(code)
81 }
82
83 #[must_use]
85 pub fn get_last_error() -> Self {
86 Self::new(unsafe { GetLastError() })
87 }
88
89 #[must_use]
91 pub const fn code(&self) -> DWORD {
92 self.0
93 }
94}
95
96impl From<DWORD> for Win32Error {
97 fn from(other: DWORD) -> Self {
98 Self::new(other)
99 }
100}
101
102impl From<Win32Error> for DWORD {
103 fn from(other: Win32Error) -> Self {
104 other.code()
105 }
106}
107
108impl Display for Win32Error {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 let mut buf = maybe_uninit_uninit_array::<u16, 1024>();
111
112 let len = unsafe {
113 FormatMessageW(
114 FORMAT_MESSAGE_FROM_SYSTEM
115 | FORMAT_MESSAGE_IGNORE_INSERTS
116 | FORMAT_MESSAGE_MAX_WIDTH_MASK,
117 ptr::null(),
118 self.0,
119 0,
120 buf[0].as_mut_ptr(),
121 buf.len() as _,
122 ptr::null_mut(),
123 )
124 } as usize;
125
126 if len == 0 {
127 write!(f, "{:#08X}", self.0)
129 } else {
130 let wide_chars = unsafe { maybe_uninit_slice_assume_init_ref(&buf[..len]) };
132 let mut char_buf = maybe_uninit_uninit_array::<char, 1024>();
133
134 let char_iter = char::decode_utf16(wide_chars.iter().copied())
135 .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER));
136
137 let mut i = 0;
138 for c in char_iter {
139 char_buf[i].write(c);
140 i += 1;
141 }
142
143 let chars = unsafe { maybe_uninit_slice_assume_init_ref(&char_buf[..i]) };
144 let start = chars.iter().position(|c| !c.is_whitespace()).unwrap_or(0);
145 let end = chars
146 .iter()
147 .rposition(|c| !c.is_whitespace())
148 .unwrap_or(chars.len());
149 for c in &chars[start..end] {
150 f.write_char(*c)?;
151 }
152
153 Ok(())
154 }
155 }
156}
157
158#[cfg(feature = "std")]
159impl Error for Win32Error {}
160
161#[cfg(feature = "std")]
162#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
164pub struct TryFromIoError;
165
166#[cfg(feature = "std")]
167impl Display for TryFromIoError {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 write!(
170 f,
171 "the given io error did not contain a windows api error code."
172 )
173 }
174}
175
176#[cfg(feature = "std")]
177impl Error for TryFromIoError {}
178
179#[cfg(feature = "std")]
180impl TryFrom<io::Error> for Win32Error {
181 type Error = TryFromIoError;
182
183 fn try_from(err: io::Error) -> Result<Self, Self::Error> {
184 err.raw_os_error()
185 .map_or_else(|| Err(TryFromIoError), |code| Ok(Self::new(code as _)))
186 }
187}
188
189#[cfg(feature = "std")]
190impl From<Win32Error> for io::Error {
191 fn from(err: Win32Error) -> Self {
192 Self::from_raw_os_error(err.code() as _)
193 }
194}
195
196const unsafe fn maybe_uninit_slice_assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T] {
198 unsafe { &*(slice as *const [MaybeUninit<T>] as *const [T]) }
199}
200
201fn maybe_uninit_uninit_array<T, const LEN: usize>() -> [MaybeUninit<T>; LEN] {
203 unsafe { MaybeUninit::<[MaybeUninit<T>; LEN]>::uninit().assume_init() }
204}