Skip to content

Commit 9c431d2

Browse files
authored
feat: downcast errors
feat: downcast errors
2 parents 95e3c7c + bf33c7d commit 9c431d2

File tree

6 files changed

+123
-61
lines changed

6 files changed

+123
-61
lines changed

examples/simple.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,10 @@ fn print(err: impl StackError) {
6262
}
6363

6464
pub mod error {
65-
use n0_error::{StackError, stack_error};
6665
use std::io;
6766

67+
use n0_error::{StackError, stack_error};
68+
6869
#[stack_error(derive, add_meta, from_sources)]
6970
pub enum OperationError {
7071
/// Failed to copy

n0-error-macros/src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,9 @@ fn generate_enum_impls(
558558
fn as_std(&self) -> &(dyn ::std::error::Error + ::std::marker::Send + ::std::marker::Sync + 'static) {
559559
self
560560
}
561-
561+
fn into_std(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn std::error::Error + ::std::marker::Send + ::std::marker::Sync> {
562+
self
563+
}
562564
fn as_dyn(&self) -> &(dyn ::n0_error::StackError) {
563565
self
564566
}
@@ -734,6 +736,9 @@ fn generate_struct_impl(
734736
fn as_std(&self) -> &(dyn ::std::error::Error + ::std::marker::Send + ::std::marker::Sync + 'static) {
735737
self
736738
}
739+
fn into_std(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn std::error::Error + ::std::marker::Send + ::std::marker::Sync> {
740+
self
741+
}
737742
fn as_dyn(&self) -> &(dyn ::n0_error::StackError) {
738743
self
739744
}

src/any.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,22 @@ impl AnyError {
9696
pub fn into_boxed_dyn_error(self) -> Box<dyn std::error::Error + Send + Sync + 'static> {
9797
Box::new(self)
9898
}
99+
100+
/// Downcast this error object by reference.
101+
pub fn downcast_ref<T: std::error::Error + 'static>(&self) -> Option<&T> {
102+
match &self.0 {
103+
Inner::Stack(err) => err.as_std().downcast_ref(),
104+
Inner::Std(err, _) => err.downcast_ref(),
105+
}
106+
}
107+
108+
/// Downcast this error object by reference.
109+
pub fn downcast<T: std::error::Error + 'static>(self) -> Option<T> {
110+
match self.0 {
111+
Inner::Stack(err) => err.into_std().downcast().ok().map(|b| *b),
112+
Inner::Std(err, _) => err.downcast().ok().map(|b| *b),
113+
}
114+
}
99115
}
100116

101117
impl fmt::Display for AnyError {
@@ -126,6 +142,9 @@ impl StackError for AnyError {
126142
fn as_dyn(&self) -> &dyn StackError {
127143
self
128144
}
145+
fn into_std(self: Box<Self>) -> Box<dyn std::error::Error + Send + Sync> {
146+
self
147+
}
129148

130149
fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
131150
match &self.0 {

src/error.rs

Lines changed: 73 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ pub trait StackError: fmt::Display + fmt::Debug + Send + Sync {
2222
/// Returns this error as a std error reference.
2323
fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static);
2424

25+
/// Returns this error as a std error.
26+
fn into_std(self: Box<Self>) -> Box<dyn std::error::Error + Send + Sync>;
27+
2528
/// Returns this error as a `dyn StackError`.
2629
fn as_dyn(&self) -> &dyn StackError;
2730

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

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

100-
pub(crate) fn std_with_meta(err: &'a dyn std::error::Error, meta: &'a Meta) -> ErrorRef<'a> {
103+
pub(crate) fn std_with_meta(
104+
err: &'a (dyn std::error::Error + 'static),
105+
meta: &'a Meta,
106+
) -> ErrorRef<'a> {
101107
ErrorRef::Std(err, Some(meta))
102108
}
103109

@@ -115,7 +121,7 @@ impl<'a> ErrorRef<'a> {
115121
}
116122

117123
/// Returns the error as a std error.
118-
pub fn as_std(&self) -> &dyn std::error::Error {
124+
pub fn as_std(self) -> &'a dyn std::error::Error {
119125
match self {
120126
ErrorRef::Std(error, _) => error,
121127
ErrorRef::Stack(error) => error.as_std(),
@@ -150,6 +156,14 @@ impl<'a> ErrorRef<'a> {
150156
ErrorRef::Stack(error) => error.fmt_message(f),
151157
}
152158
}
159+
160+
/// Downcast this error object by reference.
161+
pub fn downcast_ref<T: std::error::Error + 'static>(self) -> Option<&'a T> {
162+
match self {
163+
ErrorRef::Std(error, _) => error.downcast_ref(),
164+
ErrorRef::Stack(error) => error.as_std().downcast_ref(),
165+
}
166+
}
153167
}
154168

155169
impl<'a> fmt::Display for ErrorRef<'a> {
@@ -306,6 +320,10 @@ macro_rules! impl_stack_error_for_std_error {
306320
self
307321
}
308322

323+
fn into_std(self: Box<Self>) -> Box<dyn std::error::Error + Send + Sync> {
324+
self
325+
}
326+
309327
fn as_dyn(&self) -> &dyn StackError {
310328
self
311329
}
@@ -336,54 +354,54 @@ impl_stack_error_for_std_error!(std::string::FromUtf8Error);
336354
impl_stack_error_for_std_error!(std::net::AddrParseError);
337355
impl_stack_error_for_std_error!(std::array::TryFromSliceError);
338356

339-
impl StackError for Box<dyn StackError> {
340-
fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
341-
StackError::as_std(&**self)
342-
}
343-
344-
fn as_dyn(&self) -> &dyn StackError {
345-
StackError::as_dyn(&**self)
346-
}
347-
348-
fn meta(&self) -> Option<&Meta> {
349-
StackError::meta(&**self)
350-
}
351-
352-
fn source(&self) -> Option<ErrorRef<'_>> {
353-
StackError::source(&**self)
354-
}
355-
356-
fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357-
StackError::fmt_message(&**self, f)
358-
}
359-
360-
fn is_transparent(&self) -> bool {
361-
StackError::is_transparent(&**self)
362-
}
363-
}
364-
365-
impl StackError for std::sync::Arc<dyn StackError> {
366-
fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
367-
StackError::as_std(&**self)
368-
}
369-
370-
fn as_dyn(&self) -> &dyn StackError {
371-
StackError::as_dyn(&**self)
372-
}
373-
374-
fn meta(&self) -> Option<&Meta> {
375-
StackError::meta(&**self)
376-
}
377-
378-
fn source(&self) -> Option<ErrorRef<'_>> {
379-
StackError::source(&**self)
380-
}
381-
382-
fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383-
StackError::fmt_message(&**self, f)
384-
}
385-
386-
fn is_transparent(&self) -> bool {
387-
StackError::is_transparent(&**self)
388-
}
389-
}
357+
// impl StackError for Box<dyn StackError> {
358+
// fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
359+
// StackError::as_std(&**self)
360+
// }
361+
362+
// fn as_dyn(&self) -> &dyn StackError {
363+
// StackError::as_dyn(&**self)
364+
// }
365+
366+
// fn meta(&self) -> Option<&Meta> {
367+
// StackError::meta(&**self)
368+
// }
369+
370+
// fn source(&self) -> Option<ErrorRef<'_>> {
371+
// StackError::source(&**self)
372+
// }
373+
374+
// fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375+
// StackError::fmt_message(&**self, f)
376+
// }
377+
378+
// fn is_transparent(&self) -> bool {
379+
// StackError::is_transparent(&**self)
380+
// }
381+
// }
382+
383+
// impl StackError for std::sync::Arc<dyn StackError> {
384+
// fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
385+
// StackError::as_std(&**self)
386+
// }
387+
388+
// fn as_dyn(&self) -> &dyn StackError {
389+
// StackError::as_dyn(&**self)
390+
// }
391+
392+
// fn meta(&self) -> Option<&Meta> {
393+
// StackError::meta(&**self)
394+
// }
395+
396+
// fn source(&self) -> Option<ErrorRef<'_>> {
397+
// StackError::source(&**self)
398+
// }
399+
400+
// fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401+
// StackError::fmt_message(&**self, f)
402+
// }
403+
404+
// fn is_transparent(&self) -> bool {
405+
// StackError::is_transparent(&**self)
406+
// }
407+
// }

src/ext.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ pub trait StdResultExt<T, E> {
5858

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

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

86-
fn e(self) -> Result<T, AnyError> {
86+
fn anyerr(self) -> Result<T, AnyError> {
8787
match self {
8888
Ok(v) => Ok(v),
8989
Err(err) => Err(AnyError::from_std(err)),
@@ -114,7 +114,7 @@ impl<T> StdResultExt<T, NoneError> for Option<T> {
114114
}
115115
}
116116

117-
fn e(self) -> Result<T, AnyError> {
117+
fn anyerr(self) -> Result<T, AnyError> {
118118
match self {
119119
Some(v) => Ok(v),
120120
None => Err(NoneError { meta: meta() }.into_any()),

src/tests.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ fn test_io_err() {
119119
}
120120

121121
fn fail_outer() -> Result {
122-
fail_io().e()?;
122+
fail_io().anyerr()?;
123123
fail_custom()?;
124124
Ok(())
125125
}
@@ -512,3 +512,22 @@ fn test_generics() {
512512
});
513513
assert_eq!(format!("{err}"), "failed at foo");
514514
}
515+
516+
#[test]
517+
fn downcast() {
518+
let err = e!(MyError::A).into_any();
519+
let err_ref: &MyError = err.downcast_ref().unwrap();
520+
assert!(matches!(err_ref, MyError::A { .. }));
521+
let err: MyError = err.downcast().unwrap();
522+
assert!(matches!(err, MyError::A { .. }));
523+
524+
let err = anyerr!(io::Error::other("foo"));
525+
let err_ref: &io::Error = err.downcast_ref().unwrap();
526+
assert!(matches!(err_ref.kind(), io::ErrorKind::Other));
527+
let err: io::Error = err.downcast().unwrap();
528+
assert!(matches!(err.kind(), io::ErrorKind::Other));
529+
530+
let err = e!(MyError::A).context("foo");
531+
let err_ref: &MyError = err.source().unwrap().downcast_ref().unwrap();
532+
assert!(matches!(err_ref, MyError::A { .. }));
533+
}

0 commit comments

Comments
 (0)