use crate::api::{errors::RequireUpdateError, Error as ApiError};
use crate::WSLContext;
use std::borrow::ToOwned;
use std::ffi::{OsStr, OsString};
use std::num::NonZeroI32;
use thiserror::Error;
#[cfg(feature = "tracing")]
use tracing::debug;
use windows_core::{Error as WinError, HRESULT};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error)]
pub struct Error {
code: NonZeroI32,
message: Option<OsString>,
}
impl std::fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.message {
Some(message) => write!(
f,
"WSLPluginError {}: {}",
self.code,
message.to_string_lossy()
),
None => write!(f, "WSLPluginError {}", self.code),
}
}
}
impl Error {
#[must_use]
#[inline]
pub fn new(code: HRESULT, message: Option<&OsStr>) -> Self {
let code = if code.is_ok() {
WinError::from_hresult(code).code()
} else {
code
};
let code = unsafe { NonZeroI32::new_unchecked(code.0) };
Self {
code,
message: message.map(ToOwned::to_owned),
}
}
#[must_use]
#[inline]
pub fn with_code(code: HRESULT) -> Self {
Self::new(code, None)
}
#[must_use]
#[inline]
pub fn with_message(code: HRESULT, message: &OsStr) -> Self {
Self::new(code, Some(message))
}
#[inline]
pub const fn code(&self) -> HRESULT {
HRESULT(self.code.get())
}
#[must_use]
#[inline]
pub fn message(&self) -> Option<&OsStr> {
self.message.as_deref()
}
pub(crate) fn consume_error_message_unwrap<R: From<Self>>(self) -> R {
if let Some(ref mess) = self.message {
if let Some(context) = WSLContext::get_current() {
#[cfg_attr(not(feature = "tracing"), expect(unused_variables))]
let plugin_error_result = context.api.plugin_error(mess.as_os_str());
#[cfg(feature = "tracing")]
if let Err(err) = plugin_error_result {
debug!(
"Unable to set plugin error message {} due to error: {}",
mess.to_string_lossy(),
err
);
}
}
}
R::from(self)
}
}
impl From<Error> for WinError {
#[inline]
fn from(value: Error) -> Self {
let code = value.code();
value.message.as_ref().map_or_else(
|| Self::from_hresult(code),
|message| {
let msg_string = message.to_string_lossy();
Self::new(code, &msg_string)
},
)
}
}
impl From<WinError> for Error {
#[inline]
fn from(value: WinError) -> Self {
let os_message = if value.message().is_empty() {
None
} else {
Some(OsString::from(value.message()))
};
Self {
code: unsafe { NonZeroI32::new_unchecked(value.code().0) },
message: os_message,
}
}
}
impl From<HRESULT> for Error {
#[inline]
fn from(value: HRESULT) -> Self {
Self::new(value, None)
}
}
impl From<RequireUpdateError> for Error {
#[inline]
fn from(value: RequireUpdateError) -> Self {
Self::from(HRESULT::from(value))
}
}
impl From<ApiError> for Error {
#[inline]
fn from(value: ApiError) -> Self {
Self::from(HRESULT::from(value))
}
}