use crate::{
RawOsError,
kind::{ErrorKind, FromRawOsError},
};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[derive(Debug)]
enum ErrorPayload {
Simple,
RawOsError(RawOsError),
Message(&'static str),
#[cfg(feature = "alloc")]
Error(Box<dyn core::error::Error + Send + Sync + 'static>),
}
#[derive(Debug)]
pub struct Error<K> {
kind: K,
payload: ErrorPayload,
#[cfg(feature = "error-track_caller")]
#[expect(dead_code, reason = "Intentionally Unused Field for debugging only")]
caller_location: &'static core::panic::Location<'static>, }
impl<K: ErrorKind> core::fmt::Display for Error<K> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(&self.kind, f)?;
match &self.payload {
ErrorPayload::Simple => Ok(()),
ErrorPayload::RawOsError(os) => f.write_fmt(format_args!(" (raw os error {os})")),
ErrorPayload::Message(m) => f.write_fmt(format_args!(": {m}")),
#[cfg(feature = "alloc")]
ErrorPayload::Error(error) => f.write_fmt(format_args!(": {error}")),
}
}
}
impl<K> Error<K> {
#[cfg_attr(feature = "error-track_caller", track_caller)]
const fn internal_new(kind: K, payload: ErrorPayload) -> Self {
Self {
kind,
payload,
#[cfg(feature = "error-track_caller")]
caller_location: core::panic::Location::caller(),
}
}
#[cfg_attr(feature = "error-track_caller", track_caller)]
pub const fn new_simple(kind: K) -> Self {
Self::internal_new(kind, ErrorPayload::Simple)
}
#[cfg_attr(feature = "error-track_caller", track_caller)]
pub const fn new_with_message(kind: K, msg: &'static str) -> Self {
Self::internal_new(kind, ErrorPayload::Message(msg))
}
#[cfg_attr(feature = "error-track_caller", track_caller)]
#[cfg(feature = "alloc")]
pub fn new<E: Into<Box<dyn core::error::Error + Send + Sync + 'static>>>(
kind: K,
error: E,
) -> Self {
Self::internal_new(kind, ErrorPayload::Error(error.into()))
}
pub const fn kind(&self) -> K
where
K: Copy,
{
self.kind
}
pub fn raw_os_error(&self) -> Option<RawOsError> {
match self.payload {
ErrorPayload::RawOsError(e) => Some(e),
_ => None,
}
}
#[cfg(feature = "alloc")]
pub fn into_error(self) -> Option<Box<dyn core::error::Error + Send + Sync + 'static>> {
match self.payload {
ErrorPayload::Error(payload) => Some(payload),
ErrorPayload::Message(m) => Some(m.into()),
_ => None,
}
}
#[cfg(feature = "alloc")]
pub fn into_inner(self) -> Option<Box<dyn core::error::Error + Send + Sync + 'static>> {
match self.payload {
ErrorPayload::Error(payload) => Some(payload),
_ => None,
}
}
}
impl<K: ErrorKind> Error<K> {
#[cfg_attr(feature = "error-track_caller", track_caller)]
const fn internal_other(payload: ErrorPayload) -> Self {
Self::internal_new(K::OTHER, payload)
}
#[cfg_attr(feature = "error-track_caller", track_caller)]
pub const fn other_simple() -> Self {
Self::internal_other(ErrorPayload::Simple)
}
#[cfg_attr(feature = "error-track_caller", track_caller)]
pub const fn other_with_message(msg: &'static str) -> Self {
Self::internal_other(ErrorPayload::Message(msg))
}
#[cfg_attr(feature = "error-track_caller", track_caller)]
#[cfg(feature = "alloc")]
pub fn other<E: Into<Box<dyn core::error::Error + Send + Sync + 'static>>>(error: E) -> Self {
Self::internal_other(ErrorPayload::Error(error.into()))
}
#[cfg_attr(feature = "error-track_caller", track_caller)]
fn internal_uncategorized(payload: ErrorPayload) -> Self {
Self::internal_new(K::uncategorized(), payload)
}
#[cfg_attr(feature = "error-track_caller", track_caller)]
pub fn uncategorized_simple() -> Self {
Self::internal_uncategorized(ErrorPayload::Simple)
}
#[cfg_attr(feature = "error-track_caller", track_caller)]
pub fn uncategorized_with_message(msg: &'static str) -> Self {
Self::internal_uncategorized(ErrorPayload::Message(msg))
}
#[cfg_attr(feature = "error-track_caller", track_caller)]
#[cfg(feature = "alloc")]
pub fn uncategorized<E: Into<Box<dyn core::error::Error + Send + Sync + 'static>>>(
error: E,
) -> Self {
Self::internal_uncategorized(ErrorPayload::Error(error.into()))
}
}
impl<K: FromRawOsError> Error<K> {
#[cfg_attr(feature = "error-track_caller", track_caller)]
pub fn from_raw_os_error(error: RawOsError) -> Self {
Self::internal_new(K::from_raw_os_error(error), ErrorPayload::RawOsError(error))
}
}
impl<K: ErrorKind> core::error::Error for Error<K> {}
#[cfg(feature = "std")]
impl<K: crate::kind::FromIoKind> From<std::io::ErrorKind> for Error<K> {
#[cfg_attr(feature = "error-track_caller", track_caller)]
fn from(kind: std::io::ErrorKind) -> Self {
Error::new_simple(K::from_io_error_kind(kind))
}
}
#[cfg(feature = "std")]
impl<K: crate::kind::FromIoKind> From<std::io::Error> for Error<K> {
#[cfg_attr(feature = "error-track_caller", track_caller)]
fn from(value: std::io::Error) -> Self {
let kind = K::from_io_error_kind(value.kind());
if let Some(code) = value.raw_os_error() {
Self::internal_new(kind, ErrorPayload::RawOsError(code))
} else if let Some(data) = value.into_inner() {
Self::internal_new(kind, ErrorPayload::Error(data))
} else {
Self::internal_new(kind, ErrorPayload::Simple)
}
}
}
#[cfg(feature = "std")]
impl<K: crate::kind::IntoIoKind> From<Error<K>> for std::io::Error {
fn from(value: Error<K>) -> Self {
if let Some(code) = value.raw_os_error() {
Self::from_raw_os_error(code)
} else {
let kind = K::into_io_error_kind(value.kind());
if let Some(payload) = value.into_error() {
std::io::Error::new(kind, payload)
} else {
std::io::Error::new(kind, "(no context)")
}
}
}
}