#[cfg(feature = "alloc")]
use alloc::{boxed::Box, string::String};
use core::{any::TypeId, fmt};
enum Repr {
Simple(ErrorKind),
SimpleMessage(ErrorKind, &'static &'static str),
#[cfg(feature = "alloc")]
Custom(Box<Custom>),
}
impl fmt::Debug for Repr {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
#[cfg(feature = "alloc")]
Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt),
Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(),
Repr::SimpleMessage(kind, &message) => fmt
.debug_struct("Error")
.field("kind", &kind)
.field("message", &message)
.finish(),
}
}
}
#[cfg(feature = "alloc")]
#[derive(Debug)]
struct Custom {
kind: ErrorKind,
error: Box<dyn ErrorTrait + Send + Sync>,
}
pub trait ErrorTrait: fmt::Debug + fmt::Display {
fn source(&self) -> Option<&(dyn ErrorTrait + 'static)> {
None
}
#[doc(hidden)]
fn type_id(&self, _: private::Internal) -> TypeId
where
Self: 'static,
{
TypeId::of::<Self>()
}
}
mod private {
pub struct Internal;
}
impl dyn ErrorTrait + 'static {
#[inline]
pub fn is<T: ErrorTrait + 'static>(&self) -> bool {
let t = TypeId::of::<T>();
let boxed = self.type_id(private::Internal);
t == boxed
}
#[inline]
pub fn downcast_ref<T: ErrorTrait + 'static>(&self) -> Option<&T> {
if self.is::<T>() {
unsafe { Some(&*(self as *const dyn ErrorTrait as *const T)) }
} else {
None
}
}
#[inline]
pub fn downcast_mut<T: ErrorTrait + 'static>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
unsafe { Some(&mut *(self as *mut dyn ErrorTrait as *mut T)) }
} else {
None
}
}
}
impl dyn ErrorTrait + 'static + Send {
#[inline]
pub fn is<T: ErrorTrait + 'static>(&self) -> bool {
<dyn ErrorTrait + 'static>::is::<T>(self)
}
#[inline]
pub fn downcast_ref<T: ErrorTrait + 'static>(&self) -> Option<&T> {
<dyn ErrorTrait + 'static>::downcast_ref::<T>(self)
}
#[inline]
pub fn downcast_mut<T: ErrorTrait + 'static>(&mut self) -> Option<&mut T> {
<dyn ErrorTrait + 'static>::downcast_mut::<T>(self)
}
}
impl dyn ErrorTrait + 'static + Send + Sync {
#[inline]
pub fn is<T: ErrorTrait + 'static>(&self) -> bool {
<dyn ErrorTrait + 'static>::is::<T>(self)
}
#[inline]
pub fn downcast_ref<T: ErrorTrait + 'static>(&self) -> Option<&T> {
<dyn ErrorTrait + 'static>::downcast_ref::<T>(self)
}
#[inline]
pub fn downcast_mut<T: ErrorTrait + 'static>(&mut self) -> Option<&mut T> {
<dyn ErrorTrait + 'static>::downcast_mut::<T>(self)
}
}
#[cfg(feature = "alloc")]
impl From<String> for Box<dyn ErrorTrait + Send + Sync> {
#[inline]
fn from(err: String) -> Box<dyn ErrorTrait + Send + Sync> {
struct StringError(String);
impl ErrorTrait for StringError {}
impl fmt::Display for StringError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl fmt::Debug for StringError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
Box::new(StringError(err))
}
}
#[cfg(feature = "alloc")]
impl From<&'_ str> for Box<dyn ErrorTrait + Send + Sync> {
fn from(msg: &'_ str) -> Self {
From::from(String::from(msg))
}
}
#[cfg(feature = "alloc")]
impl<'a, E: ErrorTrait + Send + Sync + 'a> From<E> for Box<dyn ErrorTrait + Send + Sync + 'a> {
fn from(err: E) -> Box<dyn ErrorTrait + Send + Sync + 'a> {
Box::new(err)
}
}
pub struct Error {
repr: Repr,
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.repr, f)
}
}
impl Error {
#[cfg(feature = "alloc")]
pub fn new<E>(kind: ErrorKind, error: E) -> Error
where
E: Into<Box<dyn ErrorTrait + Send + Sync>>,
{
Error {
repr: Repr::Custom(Box::new(Custom {
kind,
error: error.into(),
})),
}
}
#[inline]
pub(crate) const fn new_const(kind: ErrorKind, message: &'static &'static str) -> Error {
Self {
repr: Repr::SimpleMessage(kind, message),
}
}
#[cfg(feature = "alloc")]
#[inline]
pub fn get_ref(&self) -> Option<&(dyn ErrorTrait + Send + Sync + 'static)> {
match self.repr {
Repr::Simple(..) => None,
Repr::SimpleMessage(..) => None,
Repr::Custom(ref c) => Some(&*c.error),
}
}
#[cfg(feature = "alloc")]
#[inline]
pub fn get_mut(&mut self) -> Option<&mut (dyn ErrorTrait + Send + Sync + 'static)> {
match self.repr {
Repr::Simple(..) => None,
Repr::SimpleMessage(..) => None,
Repr::Custom(ref mut c) => Some(&mut *c.error),
}
}
#[cfg(feature = "alloc")]
#[must_use = "`self` will be dropped if the result is not used"]
#[inline]
pub fn into_inner(self) -> Option<Box<dyn ErrorTrait + Send + Sync>> {
match self.repr {
Repr::Simple(..) => None,
Repr::SimpleMessage(..) => None,
Repr::Custom(c) => Some(c.error),
}
}
#[inline]
pub fn kind(&self) -> ErrorKind {
match self.repr {
#[cfg(feature = "alloc")]
Repr::Custom(ref c) => c.kind,
Repr::Simple(kind) => kind,
Repr::SimpleMessage(kind, _) => kind,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.repr {
#[cfg(feature = "alloc")]
Repr::Custom(ref c) => c.error.fmt(fmt),
Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()),
Repr::SimpleMessage(_, &msg) => msg.fmt(fmt),
}
}
}
impl ErrorTrait for Error {
fn source(&self) -> Option<&(dyn ErrorTrait + 'static)> {
match self.repr {
Repr::Simple(..) => None,
Repr::SimpleMessage(..) => None,
#[cfg(feature = "alloc")]
Repr::Custom(ref c) => c.error.source(),
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum ErrorKind {
Interrupted,
InvalidData,
InvalidInput,
UnexpectedEof,
WriteZero,
Other,
#[doc(hidden)]
Uncategorized,
}
impl ErrorKind {
pub(crate) fn as_str(&self) -> &'static str {
use ErrorKind::*;
match *self {
Interrupted => "operation interrupted",
InvalidData => "invalid data",
InvalidInput => "invalid input parameter",
Other => "other error",
Uncategorized => "uncategorized error",
UnexpectedEof => "unexpected end of file",
WriteZero => "write zero",
}
}
}
impl From<ErrorKind> for Error {
#[inline]
fn from(kind: ErrorKind) -> Error {
Error {
repr: Repr::Simple(kind),
}
}
}
pub type Result<T> = core::result::Result<T, Error>;