use std::ffi::OsString;
use std::fmt;
use std::ptr;
use winapi::shared::minwindef::DWORD;
use winapi::shared::ntdef::{LANG_NEUTRAL, LPWSTR, MAKELANGID, SUBLANG_NEUTRAL};
use winapi::shared::winerror::{HRESULT_FROM_WIN32, HRESULT, SUCCEEDED};
use winapi::um::winbase::{FormatMessageW, LocalFree, FORMAT_MESSAGE_ALLOCATE_BUFFER,
FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS};
use wio::wide::FromWide;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Error(pub HRESULT);
impl Error {
#[inline]
pub fn map<T>(hr: HRESULT, success_value: T) -> Result<T, Error> {
if SUCCEEDED(hr) {
Ok(success_value)
} else {
Err(Error(hr))
}
}
#[inline]
pub fn map_if<F, T>(hr: HRESULT, if_success: F) -> Result<T, Error> where F: FnOnce() -> T {
if SUCCEEDED(hr) {
Ok(if_success())
} else {
Err(Error(hr))
}
}
#[inline]
pub fn from_win32(err: DWORD) -> Error {
Error(HRESULT_FROM_WIN32(err))
}
#[inline]
pub fn get_message(&self) -> String {
format_err(self.0)
}
}
impl From<HRESULT> for Error {
#[inline]
fn from(hr: HRESULT) -> Error {
Error(hr)
}
}
impl fmt::Debug for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_tuple("Error")
.field(&self.0)
.field(&self.get_message())
.finish()
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(&self.get_message())
}
}
fn format_err(hr: HRESULT) -> String {
unsafe {
let flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_IGNORE_INSERTS;
let mut msg: LPWSTR = ptr::null_mut();
let len = FormatMessageW(
flags,
ptr::null_mut(),
hr as u32,
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) as u32,
(&mut msg) as *mut _ as *mut _,
0,
ptr::null_mut(),
);
if len == 0 {
return format!("Unknown Error 0x{:x}", hr);
}
let os = OsString::from_wide_ptr(msg, len as usize);
LocalFree(msg as *mut _);
os.to_string_lossy().into_owned()
}
}