#![cfg_attr(not(feature = "std"), no_std)]
#![deny(
clippy::all,
clippy::pedantic,
warnings,
future_incompatible,
rust_2018_idioms,
rustdoc,
unused,
deprecated_in_future,
missing_copy_implementations,
missing_debug_implementations,
non_ascii_idents,
trivial_casts,
trivial_numeric_casts,
unreachable_pub,
unused_import_braces,
unused_lifetimes,
unused_results
)]
#![allow(clippy::needless_doctest_main, clippy::must_use_candidate)]
#[cfg(not(target_os = "windows"))]
compile_error!("w32-error only supports Windows-based targets");
#[cfg(not(feature = "std"))]
use core as std_crate;
#[cfg(feature = "std")]
use std as std_crate;
use std_crate::{
char,
fmt::{self, Display, Formatter, Write},
hint, mem, ptr,
};
#[cfg(feature = "std")]
use std_crate::{convert::TryFrom, error::Error, io};
use winapi::{
shared::minwindef::DWORD,
um::{
errhandlingapi::GetLastError,
winbase::{
FormatMessageW, FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS,
FORMAT_MESSAGE_MAX_WIDTH_MASK,
},
winnt::WCHAR,
},
};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[must_use = "this `W32Error` is unhandled"]
#[repr(transparent)]
pub struct W32Error(DWORD);
#[cfg(feature = "std")]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[must_use = "this `TryFromIoError` is unhandled"]
pub struct TryFromIoError;
impl W32Error {
pub const fn new(code: DWORD) -> Self {
Self(code)
}
#[cfg(feature = "std")]
#[allow(clippy::cast_sign_loss)]
pub fn from_io_error(other: &io::Error) -> Option<Self> {
if let Some(code) = other.raw_os_error() {
Some(W32Error::new(code as DWORD))
} else {
None
}
}
pub fn last_thread_error() -> Self {
Self::new(unsafe { GetLastError() })
}
pub const fn into_inner(self) -> DWORD {
self.0
}
}
impl Display for W32Error {
#[allow(clippy::cast_possible_truncation)]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
const MAX_CHARACTERS: usize = 1024;
debug_assert!(mem::size_of::<[WCHAR; MAX_CHARACTERS]>() <= 65536);
let mut wide_buffer = [WCHAR::default(); MAX_CHARACTERS];
let len = unsafe {
FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK,
ptr::null(),
self.0,
0,
wide_buffer.as_mut_ptr(),
MAX_CHARACTERS as DWORD,
ptr::null_mut(),
) as usize
};
if len == 0 {
f.write_fmt(format_args!("{:#08X}", self.0))
} else {
let mut char_buffer = [char::default(); MAX_CHARACTERS];
let char_msg = &mut char_buffer[..len];
let wide_msg = &wide_buffer[..len];
char::decode_utf16(wide_msg.iter().copied())
.map(|res| res.unwrap_or(char::REPLACEMENT_CHARACTER))
.zip(char_msg.iter_mut())
.for_each(|(src, dst)| *dst = src);
if let Some(a) = char_msg.iter().position(|c| !c.is_whitespace()) {
let b = char_msg
.iter()
.rposition(|c| !c.is_whitespace())
.unwrap_or_else(|| unsafe { hint::unreachable_unchecked() });
for &c in &char_msg[a..=b] {
f.write_char(c)?;
}
}
Ok(())
}
}
}
#[cfg(feature = "std")]
impl Error for W32Error {}
#[cfg(feature = "std")]
impl From<W32Error> for io::Error {
#[allow(clippy::cast_possible_wrap)]
fn from(other: W32Error) -> Self {
io::Error::from_raw_os_error(other.into_inner() as i32)
}
}
impl From<DWORD> for W32Error {
fn from(other: DWORD) -> Self {
Self::new(other)
}
}
impl From<W32Error> for DWORD {
fn from(other: W32Error) -> Self {
other.into_inner()
}
}
#[cfg(feature = "std")]
impl TryFrom<io::Error> for W32Error {
type Error = TryFromIoError;
fn try_from(other: io::Error) -> Result<Self, Self::Error> {
Self::from_io_error(&other).ok_or(TryFromIoError)
}
}
#[cfg(feature = "std")]
impl Display for TryFromIoError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("the given `io::Error` did not contain a Windows API error code")
}
}
#[cfg(feature = "std")]
impl Error for TryFromIoError {}