Skip to content

Commit

Permalink
allow windows-result to work on linux
Browse files Browse the repository at this point in the history
  • Loading branch information
Arlie Davis committed Jun 8, 2024
1 parent 1594788 commit 504aa85
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 89 deletions.
4 changes: 0 additions & 4 deletions crates/libs/result/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ windows_targets::link!("oleaut32.dll" "system" fn SysFreeString(bstrstring : BST
windows_targets::link!("oleaut32.dll" "system" fn SysStringLen(pbstr : BSTR) -> u32);
pub type BOOL = i32;
pub type BSTR = *const u16;
pub const ERROR_INVALID_DATA: WIN32_ERROR = 13u32;
pub const ERROR_NO_UNICODE_TRANSLATION: WIN32_ERROR = 1113u32;
pub const E_INVALIDARG: HRESULT = 0x80070057_u32 as _;
pub const E_UNEXPECTED: HRESULT = 0x8000FFFF_u32 as _;
pub const FORMAT_MESSAGE_ALLOCATE_BUFFER: FORMAT_MESSAGE_OPTIONS = 256u32;
pub const FORMAT_MESSAGE_FROM_HMODULE: FORMAT_MESSAGE_OPTIONS = 2048u32;
pub const FORMAT_MESSAGE_FROM_SYSTEM: FORMAT_MESSAGE_OPTIONS = 4096u32;
Expand Down
250 changes: 203 additions & 47 deletions crates/libs/result/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,57 @@
use super::*;
use crate::HRESULT;
use core::ffi::c_void;

#[allow(dead_code)] // for #[cfg(not_yet_stable)]
mod winerror {
use crate::HRESULT;

// We define these here for non-Windows platforms.
// It's just as easy to use them on Windows, too.

// Keep sorted by numeric value
pub const E_OUTOFMEMORY: HRESULT = HRESULT::from_u32(0x8007000E);
pub const E_UNEXPECTED: HRESULT = HRESULT::from_u32(0x8000FFFF);
pub const E_FAIL: HRESULT = HRESULT::from_u32(0x80004005);
pub const E_INVALIDARG: HRESULT = HRESULT::from_u32(0x80070057);

// Keep sorted by numeric value
pub const ERROR_ACCESS_DENIED: u32 = 5;
pub const ERROR_INVALID_DATA: u32 = 13;
pub const ERROR_HANDLE_EOF: u32 = 38;
pub const ERROR_HANDLE_DISK_FULL: u32 = 39;
pub const ERROR_NOT_SUPPORTED: u32 = 50;
pub const ERROR_BROKEN_PIPE: u32 = 109;
pub const ERROR_NO_UNICODE_TRANSLATION: u32 = 1113;
pub const ERROR_POSSIBLE_DEADLOCK: u32 = 1131;
pub const ERROR_NOT_FOUND: u32 = 1168;
pub const ERROR_TIMEOUT: u32 = 1460;
pub const ERROR_DISK_QUOTA_EXCEEDED: u32 = 1295;
pub const ERROR_SEEK_ON_DEVICE: u32 = 132;
pub const ERROR_DIR_NOT_EMPTY: u32 = 145;
pub const ERROR_ALREADY_EXISTS: u32 = 183;
pub const ERROR_DIRECTORY_NOT_SUPPORTED: u32 = 336;
pub const ERROR_FILE_READ_ONLY: u32 = 6009;
pub const WSAECONNREFUSED: u32 = 10061;
pub const WSAECONNRESET: u32 = 10054;
pub const WSAECONNABORTED: u32 = 10053;
pub const WSAEHOSTUNREACH: u32 = 10065;
pub const WSAENETUNREACH: u32 = 10051;
pub const WSAENOTCONN: u32 = 10057;
pub const WSAEADDRINUSE: u32 = 10048;
pub const WSAEADDRNOTAVAIL: u32 = 10049;
pub const WSAENETDOWN: u32 = 10050;
pub const WSAEINTR: u32 = 10004;
pub const WSAEWOULDBLOCK: u32 = 10035;
}

use winerror::{ERROR_INVALID_DATA, ERROR_NO_UNICODE_TRANSLATION, E_INVALIDARG, *};

/// An error object consists of both an error code as well as detailed error information for debugging.
#[derive(Clone, PartialEq, Eq)]
pub struct Error {
code: HRESULT,
#[cfg(windows)]
info: Option<ComPtr>,
}

Expand All @@ -13,31 +60,44 @@ impl Error {
pub const fn empty() -> Self {
Self {
code: HRESULT(0),
#[cfg(windows)]
info: None,
}
}

/// Creates a new error object, capturing the stack and other information about the
/// point of failure.
pub fn new<T: AsRef<str>>(code: HRESULT, message: T) -> Self {
let message: Vec<_> = message.as_ref().encode_utf16().collect();

if message.is_empty() {
Self::from_hresult(code)
} else {
unsafe {
RoOriginateErrorW(code.0, message.len() as u32, message.as_ptr());
#[cfg(windows)]
{
let message: Vec<_> = message.as_ref().encode_utf16().collect();
if message.is_empty() {
Self::from_hresult(code)
} else {
unsafe {
RoOriginateErrorW(code.0, message.len() as u32, message.as_ptr());
}
code.into()
}
code.into()
}
#[cfg(not(windows))]
{
let _ = message;
Self::from_hresult(code)
}
}

/// Creates a new error object with an error code, but without additional error information.
pub fn from_hresult(code: HRESULT) -> Self {
Self { code, info: None }
Self {
code,
#[cfg(windows)]
info: None,
}
}

/// Creates a new `Error` from the Win32 error code returned by `GetLastError()`.
#[cfg(windows)]
pub fn from_win32() -> Self {
Self {
code: HRESULT::from_win32(unsafe { GetLastError() }),
Expand All @@ -52,49 +112,53 @@ impl Error {

/// The error message describing the error.
pub fn message(&self) -> String {
if let Some(info) = &self.info {
let mut message = BasicString::default();
#[cfg(windows)]
{
if let Some(info) = &self.info {
let mut message = BasicString::default();

// First attempt to retrieve the restricted error information.
if let Some(info) = info.cast(&IID_IRestrictedErrorInfo) {
let mut fallback = BasicString::default();
let mut code = 0;
// First attempt to retrieve the restricted error information.
if let Some(info) = info.cast(&IID_IRestrictedErrorInfo) {
let mut fallback = BasicString::default();
let mut code = 0;

unsafe {
com_call!(
IRestrictedErrorInfo_Vtbl,
info.GetErrorDetails(
&mut fallback as *mut _ as _,
&mut code,
&mut message as *mut _ as _,
&mut BasicString::default() as *mut _ as _
)
);
unsafe {
com_call!(
IRestrictedErrorInfo_Vtbl,
info.GetErrorDetails(
&mut fallback as *mut _ as _,
&mut code,
&mut message as *mut _ as _,
&mut BasicString::default() as *mut _ as _
)
);
}

if message.is_empty() {
message = fallback
};
}

// Next attempt to retrieve the regular error information.
if message.is_empty() {
message = fallback
};
}

// Next attempt to retrieve the regular error information.
if message.is_empty() {
unsafe {
com_call!(
IErrorInfo_Vtbl,
info.GetDescription(&mut message as *mut _ as _)
);
unsafe {
com_call!(
IErrorInfo_Vtbl,
info.GetDescription(&mut message as *mut _ as _)
);
}
}
}

return String::from_utf16_lossy(wide_trim_end(message.as_wide()));
return String::from_utf16_lossy(wide_trim_end(message.as_wide()));
}
}

// Otherwise fallback to a generic error code description.
self.code.message()
}

/// The error object describing the error.
#[cfg(windows)]
pub fn as_ptr(&self) -> *mut c_void {
self.info
.as_ref()
Expand All @@ -109,20 +173,31 @@ unsafe impl Sync for Error {}

impl From<Error> for HRESULT {
fn from(error: Error) -> Self {
if let Some(info) = error.info {
unsafe {
SetErrorInfo(0, info.as_raw());
#[cfg(windows)]
{
if let Some(info) = error.info {
unsafe {
SetErrorInfo(0, info.as_raw());
}
}
}
error.code
}
}

impl From<HRESULT> for Error {
#[allow(unused_mut)]
fn from(code: HRESULT) -> Self {
let mut info = None;
unsafe { GetErrorInfo(0, &mut info as *mut _ as _) };
Self { code, info }
#[cfg(windows)]
{
let mut info = None;
unsafe { GetErrorInfo(0, &mut info as *mut _ as _) };
Self { code, info }
}
#[cfg(not(windows))]
{
Self { code }
}
}
}

Expand All @@ -136,17 +211,96 @@ impl From<Error> for std::io::Error {
#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(from: std::io::Error) -> Self {
match from.raw_os_error() {
Some(status) => HRESULT::from_win32(status as u32).into(),
None => HRESULT(E_UNEXPECTED).into(),
#[cfg(windows)]
{
if let Some(status) = from.raw_os_error() {
return HRESULT::from_win32(status as u32).into();
}
}

use std::io::ErrorKind;

fn w32(code: u32) -> HRESULT {
HRESULT::from_win32(code).into()
}

// Many ErrorKind variants are not yet stabilized. Some of them have
// obvious equivalents in Win32 error codes; some do not. We list as
// many as we can here so that enabling them (as they become stable)
// is easy.

let hr: HRESULT = match from.kind() {
ErrorKind::NotFound => w32(ERROR_NOT_FOUND),
ErrorKind::PermissionDenied => w32(ERROR_ACCESS_DENIED),
ErrorKind::ConnectionRefused => w32(WSAECONNREFUSED),
ErrorKind::ConnectionReset => w32(WSAECONNRESET),
#[cfg(not_yet_stable)]
ErrorKind::HostUnreachable => w32(WSAEHOSTUNREACH),
#[cfg(not_yet_stable)]
ErrorKind::NetworkUnreachable => w32(WSAENETUNREACH),
ErrorKind::ConnectionAborted => w32(WSAECONNABORTED),
ErrorKind::NotConnected => w32(WSAENOTCONN),
ErrorKind::AddrInUse => w32(WSAEADDRINUSE),
ErrorKind::AddrNotAvailable => w32(WSAEADDRNOTAVAIL),
#[cfg(not_yet_stable)]
ErrorKind::NetworkDown => w32(WSAENETDOWN),
ErrorKind::BrokenPipe => w32(ERROR_BROKEN_PIPE),
ErrorKind::AlreadyExists => w32(ERROR_ALREADY_EXISTS),
ErrorKind::WouldBlock => w32(WSAEWOULDBLOCK),
#[cfg(not_yet_stable)]
ErrorKind::NotADirectory => _,
#[cfg(not_yet_stable)]
ErrorKind::IsADirectory => w32(ERROR_DIRECTORY_NOT_SUPPORTED),
#[cfg(not_yet_stable)]
ErrorKind::DirectoryNotEmpty => w32(ERROR_DIR_NOT_EMPTY),
#[cfg(not_yet_stable)]
ErrorKind::ReadOnlyFilesystem => w32(ERROR_FILE_READ_ONLY),
#[cfg(not_yet_stable)]
ErrorKind::FilesystemLoop => (),
#[cfg(not_yet_stable)]
ErrorKind::StaleNetworkFileHandle => (),
ErrorKind::InvalidInput => E_INVALIDARG,
ErrorKind::InvalidData => w32(ERROR_INVALID_DATA),
ErrorKind::TimedOut => w32(ERROR_TIMEOUT),
ErrorKind::WriteZero => E_FAIL,
#[cfg(not_yet_stable)]
ErrorKind::StorageFull => w32(ERROR_HANDLE_DISK_FULL),
#[cfg(not_yet_stable)]
ErrorKind::NotSeekable => w32(ERROR_SEEK_ON_DEVICE),
#[cfg(not_yet_stable)]
ErrorKind::FilesystemQuotaExceeded => w32(ERROR_DISK_QUOTA_EXCEEDED),
#[cfg(not_yet_stable)]
ErrorKind::FileTooLarge => _,
#[cfg(not_yet_stable)]
ErrorKind::ResourceBusy => _,
#[cfg(not_yet_stable)]
ErrorKind::ExecutableFileBusy => _,
#[cfg(not_yet_stable)]
ErrorKind::Deadlock => w32(ERROR_POSSIBLE_DEADLOCK),
#[cfg(not_yet_stable)]
ErrorKind::CrossesDevices => _,
#[cfg(not_yet_stable)]
ErrorKind::TooManyLinks => _,
#[cfg(not_yet_stable)]
ErrorKind::InvalidFilename => _,
#[cfg(not_yet_stable)]
ErrorKind::ArgumentListTooLong => _,
ErrorKind::Interrupted => w32(WSAEINTR),
ErrorKind::Unsupported => w32(ERROR_NOT_SUPPORTED),
ErrorKind::UnexpectedEof => w32(ERROR_HANDLE_EOF),
ErrorKind::OutOfMemory => E_OUTOFMEMORY,
ErrorKind::Other => E_FAIL,
_ => E_FAIL,
};
Self::from_hresult(hr)
}
}

impl From<alloc::string::FromUtf16Error> for Error {
fn from(_: alloc::string::FromUtf16Error) -> Self {
Self {
code: HRESULT::from_win32(ERROR_NO_UNICODE_TRANSLATION),
#[cfg(windows)]
info: None,
}
}
Expand All @@ -156,6 +310,7 @@ impl From<alloc::string::FromUtf8Error> for Error {
fn from(_: alloc::string::FromUtf8Error) -> Self {
Self {
code: HRESULT::from_win32(ERROR_NO_UNICODE_TRANSLATION),
#[cfg(windows)]
info: None,
}
}
Expand All @@ -165,6 +320,7 @@ impl From<core::num::TryFromIntError> for Error {
fn from(_: core::num::TryFromIntError) -> Self {
Self {
code: HRESULT::from_win32(ERROR_INVALID_DATA),
#[cfg(windows)]
info: None,
}
}
Expand Down
Loading

0 comments on commit 504aa85

Please sign in to comment.