mod test;
mod raw;
use crate::io::{ErrorKind, SimpleError};
use core::fmt::{self, Display, Formatter};
use core::marker::PhantomData;
#[cfg(feature = "alloc")]
use core::mem::forget;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::collections::TryReserveError;
#[cfg(feature = "alloc")]
use alloc::ffi::NulError;
#[cfg(feature = "std")]
use std::fs::TryLockError;
use raw::Raw;
#[derive(Debug)]
pub struct Error {
raw: Raw,
data: PhantomData<&'static mut (dyn core::error::Error + Send + Sync)>,
}
impl Error {
#[cfg(feature = "alloc")]
#[must_use]
pub fn new<E: Into<Box<dyn core::error::Error + Send + Sync>>>(kind: ErrorKind, source: E) -> Self {
let custom = Box::new(CustomError {
kind,
source: source.into(),
});
let raw = Raw::new_custom(custom);
Self { raw, data: PhantomData }
}
#[cfg(feature = "alloc")]
#[inline]
#[must_use]
pub fn other<E: Into<Box<dyn core::error::Error + Send + Sync>>>(source: E) -> Self {
Self::new(ErrorKind::Other, source)
}
#[cfg(feature = "std")]
#[inline]
#[must_use]
pub fn from_raw_os_error(raw: i32) -> Self {
let raw = Raw::new_os(raw);
Self { raw, data: PhantomData }
}
#[cfg(feature = "std")]
#[inline]
#[must_use]
pub fn last_os_error() -> Self {
let Some(raw) = std::io::Error::last_os_error().raw_os_error() else {
unreachable!("`last_os_error` must yield an operating system error");
};
Self::from_raw_os_error(raw)
}
#[must_use]
pub fn get_ref(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
match self.raw.data() {
#[cfg(feature = "alloc")]
ErrorData::Custom(p) => {
let r = unsafe { &*p };
Some(&*r.source)
},
_ => None,
}
}
#[must_use]
pub fn get_mut(&mut self) -> Option<&mut (dyn core::error::Error + Send + Sync + 'static)> {
match self.raw.data() {
#[cfg(feature = "alloc")]
ErrorData::Custom(p) => {
let r = unsafe { &mut *p };
Some(&mut *r.source)
},
_ => None,
}
}
#[must_use]
pub fn kind(&self) -> ErrorKind {
match self.raw.data() {
ErrorData::Simple(message) => {
message.kind()
}
ErrorData::Inline(kind) => {
kind
}
#[cfg(feature = "alloc")]
ErrorData::Custom(p) => {
let r = unsafe { &*p };
r.kind
}
#[cfg(feature = "std")]
ErrorData::Os(raw) => {
let e = std::io::Error::from_raw_os_error(raw);
e.kind().into()
}
}
}
#[inline]
#[must_use]
pub(crate) fn is_interrupted(&self) -> bool {
matches!(self.kind(), ErrorKind::Interrupted)
}
#[cfg(feature = "std")]
#[must_use]
pub fn raw_os_error(&self) -> Option<i32> {
match self.raw.data() {
ErrorData::Os(raw) => Some(raw),
_ => None,
}
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn into_inner(self) -> Option<Box<dyn core::error::Error + Send + Sync>> {
match self.raw.data() {
ErrorData::Custom(p) => {
forget(self);
let boxed = unsafe { Box::from_raw(p) };
Some(boxed.source)
}
_ => None,
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.raw.data() {
ErrorData::Simple(e) => {
Display::fmt(e, f)?;
}
ErrorData::Inline(kind) => {
Display::fmt(&kind, f)?;
}
#[cfg(feature = "alloc")]
ErrorData::Custom(p) => {
let r = unsafe { &*p };
write!(f, "{}: {}", r.kind, r.source)?;
}
#[cfg(feature = "std")]
ErrorData::Os(raw) => {
let e = std::io::Error::from_raw_os_error(raw);
Display::fmt(&e, f)?;
}
}
Ok(())
}
}
impl Drop for Error {
fn drop(&mut self) {
match self.raw.data() {
#[cfg(feature = "alloc")]
ErrorData::Custom(p) => {
let _ = unsafe { Box::from_raw(p) };
}
_ => {}
}
}
}
impl core::error::Error for Error {
#[allow(deprecated)]
fn cause(&self) -> Option<&dyn core::error::Error> {
match self.raw.data() {
#[cfg(feature = "alloc")]
ErrorData::Custom(p) => {
let r = unsafe { &*p };
r.source.cause()
}
_ => None,
}
}
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self.raw.data() {
#[cfg(feature = "alloc")]
ErrorData::Custom(p) => {
let r = unsafe { &*p };
r.source.source()
}
_ => None,
}
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
if let Some(raw) = value.raw_os_error() {
return Self::from_raw_os_error(raw);
};
let kind = ErrorKind::from(value.kind());
if let Some(source) = value.into_inner() {
return Self::new(kind, source);
};
kind.into()
}
}
impl From<ErrorKind> for Error {
#[inline]
fn from(value: ErrorKind) -> Self {
let raw = Raw::new_inline(value);
Self { raw, data: PhantomData }
}
}
#[cfg(feature = "alloc")]
impl From<NulError> for Error {
#[cfg_attr(not(feature = "std"), expect(unused))]
#[inline]
fn from(_value: NulError) -> Self {
let e = const { &SimpleError::new(ErrorKind::InvalidData, "nul octet found in c-like string") };
e.into()
}
}
impl From<&'static SimpleError> for Error {
#[inline]
fn from(value: &'static SimpleError) -> Self {
let raw = Raw::new_simple(value);
Self { raw, data: PhantomData }
}
}
#[cfg(feature = "std")]
impl From<TryLockError> for Error {
#[inline]
fn from(value: TryLockError) -> Self {
match value {
TryLockError::Error(e) => e.into(),
TryLockError::WouldBlock => ErrorKind::WouldBlock.into(),
}
}
}
#[cfg(feature = "alloc")]
impl From<TryReserveError> for Error {
#[inline]
fn from(_value: TryReserveError) -> Self {
let e = const { &SimpleError::new(ErrorKind::OutOfMemory, "unable to reserve in collection") };
e.into()
}
}
#[derive(Debug)]
pub(super) enum ErrorData {
Simple(&'static SimpleError),
Inline(ErrorKind),
#[cfg(feature = "alloc")]
Custom(*mut CustomError),
#[cfg(feature = "std")]
Os(i32),
}
#[cfg(feature = "alloc")]
#[repr(align(4))]
#[derive(Debug)]
pub(super) struct CustomError {
pub kind: ErrorKind,
pub source: Box<dyn core::error::Error + Send + Sync>,
}