#![cfg_attr(doc, doc = include_str!("../README.md"))]
#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::boxed::Box;
use core::fmt;
pub trait Source: core::error::Error + Send + Sync + 'static {}
impl<T> Source for T where T: core::error::Error + Send + Sync + 'static {}
#[cfg(feature = "alloc")]
struct ErrorMeta {
context: &'static str,
location: &'static core::panic::Location<'static>,
source: Option<Box<dyn Source>>,
}
pub struct Error<Kind> {
kind: Kind,
#[cfg(feature = "alloc")]
meta: Box<ErrorMeta>,
#[cfg(not(feature = "alloc"))]
context: &'static str,
#[cfg(not(feature = "alloc"))]
location: &'static core::panic::Location<'static>,
}
impl<Kind: fmt::Debug> fmt::Debug for Error<Kind> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut dbg = f.debug_struct("Error");
#[cfg(feature = "alloc")]
dbg.field("context", &self.meta.context)
.field("kind", &self.kind)
.field("source", &self.meta.source);
#[cfg(not(feature = "alloc"))]
dbg.field("context", &self.context).field("kind", &self.kind);
dbg.finish()
}
}
impl<Kind> Error<Kind> {
#[cold]
#[must_use]
#[track_caller]
pub fn new(context: &'static str, kind: Kind) -> Self {
Self {
kind,
#[cfg(feature = "alloc")]
meta: Box::new(ErrorMeta {
context,
location: core::panic::Location::caller(),
source: None,
}),
#[cfg(not(feature = "alloc"))]
context,
#[cfg(not(feature = "alloc"))]
location: core::panic::Location::caller(),
}
}
#[cold]
#[must_use]
pub fn with_source<E>(self, source: E) -> Self
where
E: Source,
{
#[cfg(feature = "alloc")]
{
let mut this = self;
this.meta.source = Some(Box::new(source));
this
}
#[cfg(not(feature = "alloc"))]
{
let _ = source;
self
}
}
pub fn kind(&self) -> &Kind {
&self.kind
}
pub fn location(&self) -> &'static core::panic::Location<'static> {
#[cfg(feature = "alloc")]
{
self.meta.location
}
#[cfg(not(feature = "alloc"))]
{
self.location
}
}
pub fn set_context(&mut self, context: &'static str) {
#[cfg(feature = "alloc")]
{
self.meta.context = context;
}
#[cfg(not(feature = "alloc"))]
{
self.context = context;
}
}
pub fn report(&self) -> ErrorReport<'_, Kind> {
ErrorReport(self)
}
}
impl<Kind> fmt::Display for Error<Kind>
where
Kind: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "alloc")]
{
write!(
f,
"[{} @ {}:{}] {}",
self.meta.context,
self.meta.location.file(),
self.meta.location.line(),
self.kind
)
}
#[cfg(not(feature = "alloc"))]
{
write!(
f,
"[{} @ {}:{}] {}",
self.context,
self.location.file(),
self.location.line(),
self.kind
)
}
}
}
#[cfg(feature = "std")]
impl<Kind> core::error::Error for Error<Kind>
where
Kind: core::error::Error,
{
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
if let Some(source) = self.kind.source() {
Some(source)
} else {
if let Some(e) = &self.meta.source {
Some(e.as_ref())
} else {
None
}
}
}
}
#[cfg(feature = "std")]
impl<Kind> From<Error<Kind>> for std::io::Error
where
Kind: core::error::Error + Send + Sync + 'static,
{
fn from(error: Error<Kind>) -> Self {
Self::other(error)
}
}
pub struct ErrorReport<'a, Kind>(&'a Error<Kind>);
#[cfg(feature = "std")]
impl<Kind> fmt::Display for ErrorReport<'_, Kind>
where
Kind: core::error::Error,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use core::error::Error as _;
write!(f, "{}", self.0)?;
let mut next_source = self.0.source();
while let Some(e) = next_source {
write!(f, ", caused by: {e}")?;
next_source = e.source();
}
Ok(())
}
}
#[cfg(not(feature = "std"))]
impl<E> fmt::Display for ErrorReport<'_, E>
where
E: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)?;
#[cfg(feature = "alloc")]
if let Some(source) = &self.0.meta.source {
write!(f, ", caused by: {source}")?;
}
Ok(())
}
}
#[macro_export]
macro_rules! bail {
($kind:expr $(,)?) => {
return ::core::result::Result::Err($crate::Error::new("", $kind))
};
($context:expr, $kind:expr $(,)?) => {
return ::core::result::Result::Err($crate::Error::new($context, $kind))
};
($context:expr, $kind:expr, source: $source:expr $(,)?) => {
return ::core::result::Result::Err($crate::Error::new($context, $kind).with_source($source))
};
}
#[macro_export]
macro_rules! ensure {
($condition:expr, $kind:expr $(,)?) => {
if !($condition) {
return ::core::result::Result::Err($crate::Error::new("", $kind));
}
};
($condition:expr, $context:expr, $kind:expr $(,)?) => {
if !($condition) {
return ::core::result::Result::Err($crate::Error::new($context, $kind));
}
};
}