From 25c4b5123a2bf80cc18824c0dc2ffafa70c32925 Mon Sep 17 00:00:00 2001 From: tison Date: Mon, 19 Jan 2026 09:11:58 +0800 Subject: [PATCH 1/4] feat: impl Deref for Exn + add ErrorExt::raise_all Signed-off-by: tison --- exn/src/ext.rs | 12 ++++++++++ exn/src/impls.rs | 12 ++++++++++ exn/tests/common.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 exn/tests/common.rs diff --git a/exn/src/ext.rs b/exn/src/ext.rs index c5f47d8..c1aa624 100644 --- a/exn/src/ext.rs +++ b/exn/src/ext.rs @@ -48,6 +48,18 @@ pub trait ErrorExt: Error + Send + Sync + 'static { { Exn::new(self) } + + /// Raise this error as a new exception, with `sources` as causes. + #[track_caller] + fn raise_all(self, sources: I) -> Exn + where + Self: Sized, + T: Error + Send + Sync + 'static, + I: IntoIterator, + I::Item: Into>, + { + Exn::raise_all(self, sources) + } } impl ErrorExt for T where T: Error + Send + Sync + 'static {} diff --git a/exn/src/impls.rs b/exn/src/impls.rs index 389fe74..7bcd414 100644 --- a/exn/src/impls.rs +++ b/exn/src/impls.rs @@ -20,6 +20,7 @@ use alloc::vec::Vec; use core::error::Error; use core::fmt; use core::marker::PhantomData; +use core::ops::Deref; use core::panic::Location; /// An exception type that can hold an error tree and additional context. @@ -132,6 +133,17 @@ impl Exn { } } +impl Deref for Exn +where + E: Error + Send + Sync + 'static, +{ + type Target = E; + + fn deref(&self) -> &Self::Target { + self.error() + } +} + /// A frame in the exception tree. pub struct Frame { /// The error that occurred at this frame. diff --git a/exn/tests/common.rs b/exn/tests/common.rs new file mode 100644 index 0000000..24b13fe --- /dev/null +++ b/exn/tests/common.rs @@ -0,0 +1,55 @@ +pub fn new_tree_error() -> Exn { + let e1 = Error("E1").raise(); + let e3 = e1.raise(Error("E3")); + + let e9 = Error("E9").raise(); + let e10 = e9.raise(Error("E10")); + + let e11 = Error("E11").raise(); + let e12 = e11.raise(Error("E12")); + + let e5 = Exn::raise_all([e3, e10, e12], Error("E5")); + + let e2 = Error("E2").raise(); + let e4 = e2.raise(Error("E4")); + + let e7 = Error("E7").raise(); + let e8 = e7.raise(Error("E8")); + + Exn::raise_all([e5, e4, e8], Error("E6")) +} + +#[derive(Debug)] +pub struct Error(pub &'static str); + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::error::Error for Error {} + +pub fn debug_string(input: impl std::fmt::Debug) -> String { + if cfg!(windows) { + let out = format!("{input:?}"); + out.replace('\\', "/") + } else { + format!("{input:?}") + } +} + +#[derive(Debug)] +pub struct ErrorWithSource(pub &'static str, pub Error); + +impl std::fmt::Display for ErrorWithSource { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::error::Error for ErrorWithSource { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.1) + } +} From b676261935ace6467aa080d53712c9a025dec0f7 Mon Sep 17 00:00:00 2001 From: tison Date: Mon, 19 Jan 2026 09:30:19 +0800 Subject: [PATCH 2/4] refine tests Signed-off-by: tison --- CHANGELOG.md | 2 + exn/tests/common.rs | 38 ++++-- exn/tests/main.rs | 97 ++++++++++++++ exn/tests/simple.rs | 118 ------------------ exn/tests/snapshots/main__bail.snap | 5 + exn/tests/snapshots/main__ensure_fail.snap | 5 + exn/tests/snapshots/main__from_error.snap | 5 + exn/tests/snapshots/main__linear_error.snap | 15 +++ .../snapshots/main__new_with_source.snap | 7 ++ exn/tests/snapshots/main__option_ext.snap | 5 + exn/tests/snapshots/main__result_ext.snap | 7 ++ exn/tests/snapshots/main__tree_error.snap | 29 +++++ exn/tests/snapshots/simple__bail.snap | 5 - exn/tests/snapshots/simple__ensure_fail.snap | 5 - .../simple__error_straightforward.snap | 13 -- exn/tests/snapshots/simple__error_tree.snap | 27 ---- exn/tests/snapshots/simple__from_error.snap | 5 - exn/tests/snapshots/simple__option_ext.snap | 5 - exn/tests/snapshots/simple__result_ext.snap | 7 -- 19 files changed, 204 insertions(+), 196 deletions(-) create mode 100644 exn/tests/main.rs delete mode 100644 exn/tests/simple.rs create mode 100644 exn/tests/snapshots/main__bail.snap create mode 100644 exn/tests/snapshots/main__ensure_fail.snap create mode 100644 exn/tests/snapshots/main__from_error.snap create mode 100644 exn/tests/snapshots/main__linear_error.snap create mode 100644 exn/tests/snapshots/main__new_with_source.snap create mode 100644 exn/tests/snapshots/main__option_ext.snap create mode 100644 exn/tests/snapshots/main__result_ext.snap create mode 100644 exn/tests/snapshots/main__tree_error.snap delete mode 100644 exn/tests/snapshots/simple__bail.snap delete mode 100644 exn/tests/snapshots/simple__ensure_fail.snap delete mode 100644 exn/tests/snapshots/simple__error_straightforward.snap delete mode 100644 exn/tests/snapshots/simple__error_tree.snap delete mode 100644 exn/tests/snapshots/simple__from_error.snap delete mode 100644 exn/tests/snapshots/simple__option_ext.snap delete mode 100644 exn/tests/snapshots/simple__result_ext.snap diff --git a/CHANGELOG.md b/CHANGELOG.md index 43d6f08..41f098a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,4 +12,6 @@ All significant changes to this project will be documented in this file. ### New Features +* `Exn` now implements `Deref`, allowing for more ergonomic access to the inner error. +* `ErrorExt::raise_all` supports `err.raise_all(sources)` = `Exn::raise_all(err, sources)`, which allows for raising an error with multiple sources in a more fluent way. * This crate is now `no_std` compatible, while the `alloc` crate is still required for heap allocations. It is worth noting that `no_std` support is a nice-to-have feature, and can be dropped if it blocks other important features in the future. Before 1.0, once `exn` APIs settle down, the decision on whether to keep `no_std` as a promise will be finalized. diff --git a/exn/tests/common.rs b/exn/tests/common.rs index 24b13fe..47948ca 100644 --- a/exn/tests/common.rs +++ b/exn/tests/common.rs @@ -1,3 +1,20 @@ +// Copyright 2025 FastLabs Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use exn::ErrorExt; +use exn::Exn; + pub fn new_tree_error() -> Exn { let e1 = Error("E1").raise(); let e3 = e1.raise(Error("E3")); @@ -8,7 +25,7 @@ pub fn new_tree_error() -> Exn { let e11 = Error("E11").raise(); let e12 = e11.raise(Error("E12")); - let e5 = Exn::raise_all([e3, e10, e12], Error("E5")); + let e5 = Exn::raise_all(Error("E5"), [e3, e10, e12]); let e2 = Error("E2").raise(); let e4 = e2.raise(Error("E4")); @@ -16,7 +33,15 @@ pub fn new_tree_error() -> Exn { let e7 = Error("E7").raise(); let e8 = e7.raise(Error("E8")); - Exn::raise_all([e5, e4, e8], Error("E6")) + Error("E6").raise_all([e5, e4, e8]) +} + +pub fn new_linear_error() -> Exn { + let e1 = Error("E1").raise(); + let e2 = e1.raise(Error("E2")); + let e3 = e2.raise(Error("E3")); + let e4 = e3.raise(Error("E4")); + e4.raise(Error("E5")) } #[derive(Debug)] @@ -30,15 +55,6 @@ impl std::fmt::Display for Error { impl std::error::Error for Error {} -pub fn debug_string(input: impl std::fmt::Debug) -> String { - if cfg!(windows) { - let out = format!("{input:?}"); - out.replace('\\', "/") - } else { - format!("{input:?}") - } -} - #[derive(Debug)] pub struct ErrorWithSource(pub &'static str, pub Error); diff --git a/exn/tests/main.rs b/exn/tests/main.rs new file mode 100644 index 0000000..321e7ef --- /dev/null +++ b/exn/tests/main.rs @@ -0,0 +1,97 @@ +// Copyright 2025 FastLabs Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use exn::Exn; +use exn::OptionExt; +use exn::ResultExt; + +mod common; +use common::Error; +use common::ErrorWithSource; + +#[test] +fn linear_error() { + let e = common::new_linear_error().raise(Error("topmost")); + assert_eq!(e.to_string(), "topmost"); + insta::assert_debug_snapshot!(e); +} + +#[test] +fn tree_error() { + let e = common::new_tree_error().raise(Error("topmost")); + assert_eq!(e.to_string(), "topmost"); + insta::assert_debug_snapshot!(e); +} + +#[test] +fn new_with_source() { + let e = Exn::new(ErrorWithSource("top", Error("source"))); + insta::assert_debug_snapshot!(e); +} + +#[test] +fn result_ext() { + let result: Result<(), Error> = Err(Error("An error")); + let result = result.or_raise(|| Error("Another error")); + insta::assert_debug_snapshot!(result.unwrap_err()); +} + +#[test] +fn option_ext() { + let result: Option<()> = None; + let result = result.ok_or_raise(|| Error("An error")); + insta::assert_debug_snapshot!(result.unwrap_err()); +} + +#[test] +fn from_error() { + fn foo() -> exn::Result<(), Error> { + Err(Error("An error"))?; + Ok(()) + } + + let result = foo(); + insta::assert_debug_snapshot!(result.unwrap_err()); +} + +#[test] +fn bail() { + fn foo() -> exn::Result<(), Error> { + exn::bail!(Error("An error")); + } + + let result = foo(); + insta::assert_debug_snapshot!(result.unwrap_err()); +} + +#[test] +fn ensure_ok() { + fn foo() -> exn::Result<(), Error> { + exn::ensure!(true, Error("An error")); + Ok(()) + } + + foo().unwrap(); +} + +#[test] +fn ensure_fail() { + fn foo() -> exn::Result<(), Error> { + exn::ensure!(false, Error("An error")); + Ok(()) + } + + let result = foo(); + insta::assert_debug_snapshot!(result.unwrap_err()); +} diff --git a/exn/tests/simple.rs b/exn/tests/simple.rs deleted file mode 100644 index 0c91dae..0000000 --- a/exn/tests/simple.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2025 FastLabs Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use exn::ErrorExt; -use exn::Exn; -use exn::OptionExt; -use exn::ResultExt; - -#[derive(Debug)] -struct SimpleError(&'static str); - -impl std::fmt::Display for SimpleError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl std::error::Error for SimpleError {} - -#[test] -fn test_error_straightforward() { - let e1 = SimpleError("E1").raise(); - let e2 = e1.raise(SimpleError("E2")); - let e3 = e2.raise(SimpleError("E3")); - let e4 = e3.raise(SimpleError("E4")); - let e5 = e4.raise(SimpleError("E5")); - insta::assert_debug_snapshot!(e5); -} - -#[test] -fn test_error_tree() { - let e1 = SimpleError("E1").raise(); - let e3 = e1.raise(SimpleError("E3")); - - let e9 = SimpleError("E9").raise(); - let e10 = e9.raise(SimpleError("E10")); - - let e11 = SimpleError("E11").raise(); - let e12 = e11.raise(SimpleError("E12")); - - let e5 = Exn::raise_all(SimpleError("E5"), [e3, e10, e12]); - - let e2 = SimpleError("E2").raise(); - let e4 = e2.raise(SimpleError("E4")); - - let e7 = SimpleError("E7").raise(); - let e8 = e7.raise(SimpleError("E8")); - - let e6 = Exn::raise_all(SimpleError("E6"), [e5, e4, e8]); - insta::assert_debug_snapshot!(e6); -} - -#[test] -fn test_result_ext() { - let result: Result<(), SimpleError> = Err(SimpleError("An error")); - let result = result.or_raise(|| SimpleError("Another error")); - insta::assert_debug_snapshot!(result.unwrap_err()); -} - -#[test] -fn test_option_ext() { - let result: Option<()> = None; - let result = result.ok_or_raise(|| SimpleError("An error")); - insta::assert_debug_snapshot!(result.unwrap_err()); -} - -#[test] -fn test_from_error() { - fn foo() -> exn::Result<(), SimpleError> { - Err(SimpleError("An error"))?; - Ok(()) - } - - let result = foo(); - insta::assert_debug_snapshot!(result.unwrap_err()); -} - -#[test] -fn test_bail() { - fn foo() -> exn::Result<(), SimpleError> { - exn::bail!(SimpleError("An error")); - } - - let result = foo(); - insta::assert_debug_snapshot!(result.unwrap_err()); -} - -#[test] -fn test_ensure_ok() { - fn foo() -> exn::Result<(), SimpleError> { - exn::ensure!(true, SimpleError("An error")); - Ok(()) - } - - foo().unwrap(); -} - -#[test] -fn test_ensure_fail() { - fn foo() -> exn::Result<(), SimpleError> { - exn::ensure!(false, SimpleError("An error")); - Ok(()) - } - - let result = foo(); - insta::assert_debug_snapshot!(result.unwrap_err()); -} diff --git a/exn/tests/snapshots/main__bail.snap b/exn/tests/snapshots/main__bail.snap new file mode 100644 index 0000000..4e21f38 --- /dev/null +++ b/exn/tests/snapshots/main__bail.snap @@ -0,0 +1,5 @@ +--- +source: exn/tests/main.rs +expression: result.unwrap_err() +--- +An error, at exn/tests/main.rs:71:9 diff --git a/exn/tests/snapshots/main__ensure_fail.snap b/exn/tests/snapshots/main__ensure_fail.snap new file mode 100644 index 0000000..fe44aff --- /dev/null +++ b/exn/tests/snapshots/main__ensure_fail.snap @@ -0,0 +1,5 @@ +--- +source: exn/tests/main.rs +expression: result.unwrap_err() +--- +An error, at exn/tests/main.rs:91:9 diff --git a/exn/tests/snapshots/main__from_error.snap b/exn/tests/snapshots/main__from_error.snap new file mode 100644 index 0000000..bf0bf47 --- /dev/null +++ b/exn/tests/snapshots/main__from_error.snap @@ -0,0 +1,5 @@ +--- +source: exn/tests/main.rs +expression: result.unwrap_err() +--- +An error, at exn/tests/main.rs:60:9 diff --git a/exn/tests/snapshots/main__linear_error.snap b/exn/tests/snapshots/main__linear_error.snap new file mode 100644 index 0000000..c89d4a5 --- /dev/null +++ b/exn/tests/snapshots/main__linear_error.snap @@ -0,0 +1,15 @@ +--- +source: exn/tests/main.rs +expression: e +--- +topmost, at exn/tests/main.rs:25:40 +| +|-> E5, at exn/tests/common.rs:44:8 +| +|-> E4, at exn/tests/common.rs:43:17 +| +|-> E3, at exn/tests/common.rs:42:17 +| +|-> E2, at exn/tests/common.rs:41:17 +| +|-> E1, at exn/tests/common.rs:40:26 diff --git a/exn/tests/snapshots/main__new_with_source.snap b/exn/tests/snapshots/main__new_with_source.snap new file mode 100644 index 0000000..ca4e46f --- /dev/null +++ b/exn/tests/snapshots/main__new_with_source.snap @@ -0,0 +1,7 @@ +--- +source: exn/tests/main.rs +expression: e +--- +top, at exn/tests/main.rs:39:13 +| +|-> source, at exn/tests/main.rs:39:13 diff --git a/exn/tests/snapshots/main__option_ext.snap b/exn/tests/snapshots/main__option_ext.snap new file mode 100644 index 0000000..efea6e7 --- /dev/null +++ b/exn/tests/snapshots/main__option_ext.snap @@ -0,0 +1,5 @@ +--- +source: exn/tests/main.rs +expression: result.unwrap_err() +--- +An error, at exn/tests/main.rs:53:25 diff --git a/exn/tests/snapshots/main__result_ext.snap b/exn/tests/snapshots/main__result_ext.snap new file mode 100644 index 0000000..4d57554 --- /dev/null +++ b/exn/tests/snapshots/main__result_ext.snap @@ -0,0 +1,7 @@ +--- +source: exn/tests/main.rs +expression: result.unwrap_err() +--- +Another error, at exn/tests/main.rs:46:25 +| +|-> An error, at exn/tests/main.rs:46:25 diff --git a/exn/tests/snapshots/main__tree_error.snap b/exn/tests/snapshots/main__tree_error.snap new file mode 100644 index 0000000..01ea691 --- /dev/null +++ b/exn/tests/snapshots/main__tree_error.snap @@ -0,0 +1,29 @@ +--- +source: exn/tests/main.rs +expression: e +--- +topmost, at exn/tests/main.rs:32:38 +| +|-> E6, at exn/tests/common.rs:36:17 + | + |-> E5, at exn/tests/common.rs:28:14 + | | + | |-> E3, at exn/tests/common.rs:20:17 + | | | + | | |-> E1, at exn/tests/common.rs:19:26 + | | + | |-> E10, at exn/tests/common.rs:23:18 + | | | + | | |-> E9, at exn/tests/common.rs:22:26 + | | + | |-> E12, at exn/tests/common.rs:26:19 + | | + | |-> E11, at exn/tests/common.rs:25:28 + | + |-> E4, at exn/tests/common.rs:31:17 + | | + | |-> E2, at exn/tests/common.rs:30:26 + | + |-> E8, at exn/tests/common.rs:34:17 + | + |-> E7, at exn/tests/common.rs:33:26 diff --git a/exn/tests/snapshots/simple__bail.snap b/exn/tests/snapshots/simple__bail.snap deleted file mode 100644 index c34b677..0000000 --- a/exn/tests/snapshots/simple__bail.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: exn/tests/simple.rs -expression: result.unwrap_err() ---- -An error, at exn/tests/simple.rs:92:9 diff --git a/exn/tests/snapshots/simple__ensure_fail.snap b/exn/tests/snapshots/simple__ensure_fail.snap deleted file mode 100644 index e159214..0000000 --- a/exn/tests/snapshots/simple__ensure_fail.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: exn/tests/simple.rs -expression: result.unwrap_err() ---- -An error, at exn/tests/simple.rs:112:9 diff --git a/exn/tests/snapshots/simple__error_straightforward.snap b/exn/tests/snapshots/simple__error_straightforward.snap deleted file mode 100644 index e772fed..0000000 --- a/exn/tests/snapshots/simple__error_straightforward.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: exn/tests/simple.rs -expression: e5 ---- -E5, at exn/tests/simple.rs:37:17 -| -|-> E4, at exn/tests/simple.rs:36:17 -| -|-> E3, at exn/tests/simple.rs:35:17 -| -|-> E2, at exn/tests/simple.rs:34:17 -| -|-> E1, at exn/tests/simple.rs:33:32 diff --git a/exn/tests/snapshots/simple__error_tree.snap b/exn/tests/snapshots/simple__error_tree.snap deleted file mode 100644 index 943f4ce..0000000 --- a/exn/tests/snapshots/simple__error_tree.snap +++ /dev/null @@ -1,27 +0,0 @@ ---- -source: exn/tests/simple.rs -expression: e6 ---- -E6, at exn/tests/simple.rs:60:14 -| -|-> E5, at exn/tests/simple.rs:52:14 -| | -| |-> E3, at exn/tests/simple.rs:44:17 -| | | -| | |-> E1, at exn/tests/simple.rs:43:32 -| | -| |-> E10, at exn/tests/simple.rs:47:18 -| | | -| | |-> E9, at exn/tests/simple.rs:46:32 -| | -| |-> E12, at exn/tests/simple.rs:50:19 -| | -| |-> E11, at exn/tests/simple.rs:49:34 -| -|-> E4, at exn/tests/simple.rs:55:17 -| | -| |-> E2, at exn/tests/simple.rs:54:32 -| -|-> E8, at exn/tests/simple.rs:58:17 - | - |-> E7, at exn/tests/simple.rs:57:32 diff --git a/exn/tests/snapshots/simple__from_error.snap b/exn/tests/snapshots/simple__from_error.snap deleted file mode 100644 index cb43ac5..0000000 --- a/exn/tests/snapshots/simple__from_error.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: exn/tests/simple.rs -expression: result.unwrap_err() ---- -An error, at exn/tests/simple.rs:81:9 diff --git a/exn/tests/snapshots/simple__option_ext.snap b/exn/tests/snapshots/simple__option_ext.snap deleted file mode 100644 index d5d3390..0000000 --- a/exn/tests/snapshots/simple__option_ext.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: exn/tests/simple.rs -expression: result.unwrap_err() ---- -An error, at exn/tests/simple.rs:74:25 diff --git a/exn/tests/snapshots/simple__result_ext.snap b/exn/tests/snapshots/simple__result_ext.snap deleted file mode 100644 index 013566e..0000000 --- a/exn/tests/snapshots/simple__result_ext.snap +++ /dev/null @@ -1,7 +0,0 @@ ---- -source: exn/tests/simple.rs -expression: result.unwrap_err() ---- -Another error, at exn/tests/simple.rs:67:25 -| -|-> An error, at exn/tests/simple.rs:67:25 From 26d8c7c988b70dba6a7ad895d0cb03076d6ece87 Mon Sep 17 00:00:00 2001 From: tison Date: Mon, 19 Jan 2026 12:47:45 +0800 Subject: [PATCH 3/4] more Signed-off-by: tison --- CHANGELOG.md | 1 - exn/src/ext.rs | 12 ------------ exn/tests/common.rs | 2 +- exn/tests/snapshots/main__tree_error.snap | 2 +- 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41f098a..880789c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,5 +13,4 @@ All significant changes to this project will be documented in this file. ### New Features * `Exn` now implements `Deref`, allowing for more ergonomic access to the inner error. -* `ErrorExt::raise_all` supports `err.raise_all(sources)` = `Exn::raise_all(err, sources)`, which allows for raising an error with multiple sources in a more fluent way. * This crate is now `no_std` compatible, while the `alloc` crate is still required for heap allocations. It is worth noting that `no_std` support is a nice-to-have feature, and can be dropped if it blocks other important features in the future. Before 1.0, once `exn` APIs settle down, the decision on whether to keep `no_std` as a promise will be finalized. diff --git a/exn/src/ext.rs b/exn/src/ext.rs index c1aa624..c5f47d8 100644 --- a/exn/src/ext.rs +++ b/exn/src/ext.rs @@ -48,18 +48,6 @@ pub trait ErrorExt: Error + Send + Sync + 'static { { Exn::new(self) } - - /// Raise this error as a new exception, with `sources` as causes. - #[track_caller] - fn raise_all(self, sources: I) -> Exn - where - Self: Sized, - T: Error + Send + Sync + 'static, - I: IntoIterator, - I::Item: Into>, - { - Exn::raise_all(self, sources) - } } impl ErrorExt for T where T: Error + Send + Sync + 'static {} diff --git a/exn/tests/common.rs b/exn/tests/common.rs index 47948ca..9b83206 100644 --- a/exn/tests/common.rs +++ b/exn/tests/common.rs @@ -33,7 +33,7 @@ pub fn new_tree_error() -> Exn { let e7 = Error("E7").raise(); let e8 = e7.raise(Error("E8")); - Error("E6").raise_all([e5, e4, e8]) + Exn::raise_all(Error("E6"), [e5, e4, e8]) } pub fn new_linear_error() -> Exn { diff --git a/exn/tests/snapshots/main__tree_error.snap b/exn/tests/snapshots/main__tree_error.snap index 01ea691..7ab4ae4 100644 --- a/exn/tests/snapshots/main__tree_error.snap +++ b/exn/tests/snapshots/main__tree_error.snap @@ -4,7 +4,7 @@ expression: e --- topmost, at exn/tests/main.rs:32:38 | -|-> E6, at exn/tests/common.rs:36:17 +|-> E6, at exn/tests/common.rs:36:5 | |-> E5, at exn/tests/common.rs:28:14 | | From 2286610c58e1f126bea22ef0309693df9e97592e Mon Sep 17 00:00:00 2001 From: tison Date: Mon, 19 Jan 2026 13:12:02 +0800 Subject: [PATCH 4/4] no Exn::error Signed-off-by: tison --- CHANGELOG.md | 1 + exn/src/display.rs | 3 ++- exn/src/impls.rs | 13 ++++--------- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 880789c..8389928 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All significant changes to this project will be documented in this file. * `Exn::from_iter` has been renamed to `Exn::raise_all` * `exn::Error` trait bound has been removed in favor of inlined `StdError + Send + Sync + 'static` bounds. * `err.raise()` has been moved to the `exn::ErrorExt` extension trait. +* `Exn::error(&self)` has been replaced with `impl Deref for Exn`. ### New Features diff --git a/exn/src/display.rs b/exn/src/display.rs index d0c6380..928f09e 100644 --- a/exn/src/display.rs +++ b/exn/src/display.rs @@ -14,13 +14,14 @@ use core::error::Error; use core::fmt; +use core::ops::Deref; use crate::Exn; use crate::Frame; impl fmt::Display for Exn { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.error()) + write!(f, "{}", self.deref()) } } diff --git a/exn/src/impls.rs b/exn/src/impls.rs index 7bcd414..da7ee63 100644 --- a/exn/src/impls.rs +++ b/exn/src/impls.rs @@ -119,14 +119,6 @@ impl Exn { new_exn } - /// Return the current exception. - pub fn error(&self) -> &E { - self.frame - .error() - .downcast_ref() - .expect("error type must match") - } - /// Return the underlying exception frame. pub fn frame(&self) -> &Frame { &self.frame @@ -140,7 +132,10 @@ where type Target = E; fn deref(&self) -> &Self::Target { - self.error() + self.frame + .error() + .downcast_ref() + .expect("error type must match") } }