Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ fn print(err: impl StackError) {
}

pub mod error {
use n0_error::{StackError, stack_error};
use std::io;

use n0_error::{StackError, stack_error};

#[stack_error(derive, add_meta, from_sources)]
pub enum OperationError {
/// Failed to copy
Expand Down
7 changes: 6 additions & 1 deletion n0-error-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,9 @@ fn generate_enum_impls(
fn as_std(&self) -> &(dyn ::std::error::Error + ::std::marker::Send + ::std::marker::Sync + 'static) {
self
}

fn into_std(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn std::error::Error + ::std::marker::Send + ::std::marker::Sync> {
self
}
fn as_dyn(&self) -> &(dyn ::n0_error::StackError) {
self
}
Expand Down Expand Up @@ -734,6 +736,9 @@ fn generate_struct_impl(
fn as_std(&self) -> &(dyn ::std::error::Error + ::std::marker::Send + ::std::marker::Sync + 'static) {
self
}
fn into_std(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn std::error::Error + ::std::marker::Send + ::std::marker::Sync> {
self
}
fn as_dyn(&self) -> &(dyn ::n0_error::StackError) {
self
}
Expand Down
19 changes: 19 additions & 0 deletions src/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,22 @@ impl AnyError {
pub fn into_boxed_dyn_error(self) -> Box<dyn std::error::Error + Send + Sync + 'static> {
Box::new(self)
}

/// Downcast this error object by reference.
pub fn downcast_ref<T: std::error::Error + 'static>(&self) -> Option<&T> {
match &self.0 {
Inner::Stack(err) => err.as_std().downcast_ref(),
Inner::Std(err, _) => err.downcast_ref(),
}
}

/// Downcast this error object by reference.
pub fn downcast<T: std::error::Error + 'static>(self) -> Option<T> {
match self.0 {
Inner::Stack(err) => err.into_std().downcast().ok().map(|b| *b),
Inner::Std(err, _) => err.downcast().ok().map(|b| *b),
}
}
}

impl fmt::Display for AnyError {
Expand Down Expand Up @@ -126,6 +142,9 @@ impl StackError for AnyError {
fn as_dyn(&self) -> &dyn StackError {
self
}
fn into_std(self: Box<Self>) -> Box<dyn std::error::Error + Send + Sync> {
self
}

fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
match &self.0 {
Expand Down
128 changes: 73 additions & 55 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub trait StackError: fmt::Display + fmt::Debug + Send + Sync {
/// Returns this error as a std error reference.
fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static);

/// Returns this error as a std error.
fn into_std(self: Box<Self>) -> Box<dyn std::error::Error + Send + Sync>;

/// Returns this error as a `dyn StackError`.
fn as_dyn(&self) -> &dyn StackError;

Expand Down Expand Up @@ -86,18 +89,21 @@ impl<T: StackError + Sized + 'static> StackErrorExt for T {}
#[derive(Copy, Clone, Debug)]
pub enum ErrorRef<'a> {
/// Std error (no location info).
Std(&'a dyn std::error::Error, Option<&'a Meta>),
Std(&'a (dyn std::error::Error + 'static), Option<&'a Meta>),
/// StackError (has location info).
Stack(&'a dyn StackError),
}

impl<'a> ErrorRef<'a> {
/// Creates a [`ErrorRef`] for a std error.
pub fn std(err: &dyn std::error::Error) -> ErrorRef<'_> {
pub fn std(err: &'a (dyn std::error::Error + 'static)) -> ErrorRef<'a> {
ErrorRef::Std(err, None)
}

pub(crate) fn std_with_meta(err: &'a dyn std::error::Error, meta: &'a Meta) -> ErrorRef<'a> {
pub(crate) fn std_with_meta(
err: &'a (dyn std::error::Error + 'static),
meta: &'a Meta,
) -> ErrorRef<'a> {
ErrorRef::Std(err, Some(meta))
}

Expand All @@ -115,7 +121,7 @@ impl<'a> ErrorRef<'a> {
}

/// Returns the error as a std error.
pub fn as_std(&self) -> &dyn std::error::Error {
pub fn as_std(self) -> &'a dyn std::error::Error {
match self {
ErrorRef::Std(error, _) => error,
ErrorRef::Stack(error) => error.as_std(),
Expand Down Expand Up @@ -150,6 +156,14 @@ impl<'a> ErrorRef<'a> {
ErrorRef::Stack(error) => error.fmt_message(f),
}
}

/// Downcast this error object by reference.
pub fn downcast_ref<T: std::error::Error + 'static>(self) -> Option<&'a T> {
match self {
ErrorRef::Std(error, _) => error.downcast_ref(),
ErrorRef::Stack(error) => error.as_std().downcast_ref(),
}
}
}

impl<'a> fmt::Display for ErrorRef<'a> {
Expand Down Expand Up @@ -306,6 +320,10 @@ macro_rules! impl_stack_error_for_std_error {
self
}

fn into_std(self: Box<Self>) -> Box<dyn std::error::Error + Send + Sync> {
self
}

fn as_dyn(&self) -> &dyn StackError {
self
}
Expand Down Expand Up @@ -336,54 +354,54 @@ impl_stack_error_for_std_error!(std::string::FromUtf8Error);
impl_stack_error_for_std_error!(std::net::AddrParseError);
impl_stack_error_for_std_error!(std::array::TryFromSliceError);

impl StackError for Box<dyn StackError> {
fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
StackError::as_std(&**self)
}

fn as_dyn(&self) -> &dyn StackError {
StackError::as_dyn(&**self)
}

fn meta(&self) -> Option<&Meta> {
StackError::meta(&**self)
}

fn source(&self) -> Option<ErrorRef<'_>> {
StackError::source(&**self)
}

fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
StackError::fmt_message(&**self, f)
}

fn is_transparent(&self) -> bool {
StackError::is_transparent(&**self)
}
}

impl StackError for std::sync::Arc<dyn StackError> {
fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
StackError::as_std(&**self)
}

fn as_dyn(&self) -> &dyn StackError {
StackError::as_dyn(&**self)
}

fn meta(&self) -> Option<&Meta> {
StackError::meta(&**self)
}

fn source(&self) -> Option<ErrorRef<'_>> {
StackError::source(&**self)
}

fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
StackError::fmt_message(&**self, f)
}

fn is_transparent(&self) -> bool {
StackError::is_transparent(&**self)
}
}
// impl StackError for Box<dyn StackError> {
// fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
// StackError::as_std(&**self)
// }

// fn as_dyn(&self) -> &dyn StackError {
// StackError::as_dyn(&**self)
// }

// fn meta(&self) -> Option<&Meta> {
// StackError::meta(&**self)
// }

// fn source(&self) -> Option<ErrorRef<'_>> {
// StackError::source(&**self)
// }

// fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// StackError::fmt_message(&**self, f)
// }

// fn is_transparent(&self) -> bool {
// StackError::is_transparent(&**self)
// }
// }

// impl StackError for std::sync::Arc<dyn StackError> {
// fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
// StackError::as_std(&**self)
// }

// fn as_dyn(&self) -> &dyn StackError {
// StackError::as_dyn(&**self)
// }

// fn meta(&self) -> Option<&Meta> {
// StackError::meta(&**self)
// }

// fn source(&self) -> Option<ErrorRef<'_>> {
// StackError::source(&**self)
// }

// fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// StackError::fmt_message(&**self, f)
// }

// fn is_transparent(&self) -> bool {
// StackError::is_transparent(&**self)
// }
// }
6 changes: 3 additions & 3 deletions src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub trait StdResultExt<T, E> {

/// Converts the result's error into [`AnyError`].
#[track_caller]
fn e(self) -> Result<T, AnyError>;
fn anyerr(self) -> Result<T, AnyError>;
}

impl<T, E: std::error::Error + Send + Sync + 'static> StdResultExt<T, E> for Result<T, E> {
Expand All @@ -83,7 +83,7 @@ impl<T, E: std::error::Error + Send + Sync + 'static> StdResultExt<T, E> for Res
}
}

fn e(self) -> Result<T, AnyError> {
fn anyerr(self) -> Result<T, AnyError> {
match self {
Ok(v) => Ok(v),
Err(err) => Err(AnyError::from_std(err)),
Expand Down Expand Up @@ -114,7 +114,7 @@ impl<T> StdResultExt<T, NoneError> for Option<T> {
}
}

fn e(self) -> Result<T, AnyError> {
fn anyerr(self) -> Result<T, AnyError> {
match self {
Some(v) => Ok(v),
None => Err(NoneError { meta: meta() }.into_any()),
Expand Down
21 changes: 20 additions & 1 deletion src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ fn test_io_err() {
}

fn fail_outer() -> Result {
fail_io().e()?;
fail_io().anyerr()?;
fail_custom()?;
Ok(())
}
Expand Down Expand Up @@ -512,3 +512,22 @@ fn test_generics() {
});
assert_eq!(format!("{err}"), "failed at foo");
}

#[test]
fn downcast() {
let err = e!(MyError::A).into_any();
let err_ref: &MyError = err.downcast_ref().unwrap();
assert!(matches!(err_ref, MyError::A { .. }));
let err: MyError = err.downcast().unwrap();
assert!(matches!(err, MyError::A { .. }));

let err = anyerr!(io::Error::other("foo"));
let err_ref: &io::Error = err.downcast_ref().unwrap();
assert!(matches!(err_ref.kind(), io::ErrorKind::Other));
let err: io::Error = err.downcast().unwrap();
assert!(matches!(err.kind(), io::ErrorKind::Other));

let err = e!(MyError::A).context("foo");
let err_ref: &MyError = err.source().unwrap().downcast_ref().unwrap();
assert!(matches!(err_ref, MyError::A { .. }));
}
Loading