diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 58b6ce6..f282003 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -37,7 +37,7 @@ path = "src/downcast.rs" [package.metadata.release] release = false -[dependencies] +[dev-dependencies] derive_more = { workspace = true } exn = { workspace = true } diff --git a/examples/src/custom-layout.rs b/examples/src/custom-layout.rs index 36eb54e..348dc81 100644 --- a/examples/src/custom-layout.rs +++ b/examples/src/custom-layout.rs @@ -17,6 +17,7 @@ //! This example shows how to traverse the error chain and create custom //! formatting to match your application's needs. +use std::error::Error; use std::fmt::Write; use exn::Exn; @@ -48,10 +49,10 @@ impl std::error::Error for MainError {} impl MainError { /// Convert an `Exn` into MainError with custom numbered list formatting. - pub fn new(err: Exn) -> Self { + pub fn new(err: Exn) -> Self { fn collect_frames(frame: &Frame, frames: &mut Vec) { // Add this frame first - frames.push(format!("[{}] {}", frame.location(), frame.as_error())); + frames.push(format!("[{}] {}", frame.location(), frame.error())); // Then collect children for child in frame.children() { collect_frames(child, frames); @@ -59,7 +60,7 @@ impl MainError { } let mut frames = vec![]; - collect_frames(err.as_frame(), &mut frames); + collect_frames(err.frame(), &mut frames); // Format as numbered list let mut report = String::new(); @@ -120,5 +121,5 @@ mod http { // Output when running `cargo run --example custom_layout`: // // Error: fatal error occurred in application: -// 0: [examples/src/custom-layout.rs:81:30] failed to run app -// 1: [examples/src/custom-layout.rs:101:9] failed to send request to server: https://example.com +// 0: [examples/src/custom-layout.rs:82:30] failed to run app +// 1: [examples/src/custom-layout.rs:102:9] failed to send request to server: https://example.com diff --git a/examples/src/downcast.rs b/examples/src/downcast.rs index 45d6645..8e2f308 100644 --- a/examples/src/downcast.rs +++ b/examples/src/downcast.rs @@ -21,6 +21,8 @@ //! - Recover from specific error types //! - Extract structured data (HTTP codes, retry hints, etc.) +use std::error::Error; + use derive_more::Display; use exn::Exn; use exn::Frame; @@ -54,10 +56,10 @@ fn main() -> Result<(), MainError> { } /// Walk the error chain and extract HTTP status code if present. -fn extract_http_status(err: &Exn) -> Option { +fn extract_http_status(err: &Exn) -> Option { fn walk(frame: &Frame) -> Option { // Try to downcast current frame - if let Some(http_err) = frame.as_any().downcast_ref::() { + if let Some(http_err) = frame.error().downcast_ref::() { return Some(http_err.status); } @@ -65,7 +67,7 @@ fn extract_http_status(err: &Exn) -> Option { frame.children().iter().find_map(walk) } - walk(err.as_frame()) + walk(err.frame()) } #[derive(Debug, Display)] @@ -117,8 +119,8 @@ mod http { // Retryable error, attempting retry #3 // // HTTP error with status code: 503 -// Error: fatal error occurred in application, at examples/src/downcast.rs:52:24 +// Error: fatal error occurred in application, at examples/src/downcast.rs:54:24 // | -// |-> failed to run app, at examples/src/downcast.rs:80:35 +// |-> failed to run app, at examples/src/downcast.rs:82:35 // | -// |-> HTTP 503: service unavailable, at examples/src/downcast.rs:93:9 +// |-> HTTP 503: service unavailable, at examples/src/downcast.rs:95:9 diff --git a/exn/Cargo.toml b/exn/Cargo.toml index 8380748..ab9ca26 100644 --- a/exn/Cargo.toml +++ b/exn/Cargo.toml @@ -28,8 +28,6 @@ repository.workspace = true all-features = true rustdoc-args = ["--cfg", "docsrs"] -[dependencies] - [dev-dependencies] insta = { workspace = true } diff --git a/exn/src/debug.rs b/exn/src/debug.rs index e45ea7e..04c556e 100644 --- a/exn/src/debug.rs +++ b/exn/src/debug.rs @@ -12,16 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::error::Error; use std::fmt; use std::fmt::Formatter; -use crate::Error; use crate::Exn; use crate::Frame; impl fmt::Debug for Exn { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write_exn(f, self.as_frame(), 0, "") + write_exn(f, self.frame(), 0, "") } } @@ -32,7 +32,7 @@ impl fmt::Debug for Frame { } fn write_exn(f: &mut Formatter<'_>, frame: &Frame, level: usize, prefix: &str) -> fmt::Result { - write!(f, "{}", frame.as_error())?; + write!(f, "{}", frame.error())?; write_location(f, frame)?; let children = frame.children(); diff --git a/exn/src/display.rs b/exn/src/display.rs index 7b839d6..80c8875 100644 --- a/exn/src/display.rs +++ b/exn/src/display.rs @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::error::Error; use std::fmt; -use crate::Error; use crate::Exn; impl fmt::Display for Exn { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_error()) + write!(f, "{}", self.error()) } } diff --git a/exn/src/impls.rs b/exn/src/impls.rs index fadf1fb..43ed0cd 100644 --- a/exn/src/impls.rs +++ b/exn/src/impls.rs @@ -12,35 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::error::Error; use std::fmt; use std::marker::PhantomData; use std::panic::Location; -use crate::Error; - /// An exception type that can hold an error tree and additional context. -pub struct Exn { +pub struct Exn { // trade one more indirection for less stack size frame: Box, phantom: PhantomData, } -impl From for Exn { +impl From for Exn { #[track_caller] fn from(error: E) -> Self { Exn::new(error) } } -impl Exn { +impl Exn { /// Create a new exception with the given error. /// /// This will automatically walk the [source chain of the error] and add them as children /// frames. /// - /// See also [`Error::raise`] for a fluent way to convert an error into an `Exn` instance. - /// - /// [source chain of the error]: std::error::Error::source + /// [source chain of the error]: Error::source #[track_caller] pub fn new(error: E) -> Self { struct SourceError(String); @@ -57,9 +54,9 @@ impl Exn { } } - impl std::error::Error for SourceError {} + impl Error for SourceError {} - fn walk(error: &dyn std::error::Error, location: &'static Location<'static>) -> Vec { + fn walk(error: &dyn Error, location: &'static Location<'static>) -> Vec { if let Some(source) = error.source() { let children = vec![Frame { error: Box::new(SourceError(source.to_string())), @@ -90,7 +87,7 @@ impl Exn { #[track_caller] pub fn from_iter(children: I, err: E) -> Self where - T: Error, + T: Error + 'static, I: IntoIterator, I::Item: Into>, { @@ -111,15 +108,15 @@ impl Exn { } /// Return the current exception. - pub fn as_error(&self) -> &E { + pub fn error(&self) -> &E { self.frame - .as_any() + .error() .downcast_ref() .expect("error type must match") } /// Return the underlying exception frame. - pub fn as_frame(&self) -> &Frame { + pub fn frame(&self) -> &Frame { &self.frame } } @@ -127,7 +124,7 @@ impl Exn { /// A frame in the exception tree. pub struct Frame { /// The error that occurred at this frame. - error: Box, + error: Box, /// The source code location where this exception frame was created. location: &'static Location<'static>, /// Child exception frames that provide additional context or source errors. @@ -135,13 +132,8 @@ pub struct Frame { } impl Frame { - /// Return the error as a reference to [`std::any::Any`]. - pub fn as_any(&self) -> &dyn std::any::Any { - &*self.error - } - - /// Return the error as a reference to [`std::error::Error`]. - pub fn as_error(&self) -> &dyn std::error::Error { + /// Return the error that occurred at this frame. + pub fn error(&self) -> &(dyn Error + 'static) { &*self.error } diff --git a/exn/src/lib.rs b/exn/src/lib.rs index c3cc71b..46ecec8 100644 --- a/exn/src/lib.rs +++ b/exn/src/lib.rs @@ -88,20 +88,6 @@ pub use self::option::OptionExt; pub use self::result::Result; pub use self::result::ResultExt; -/// A trait bound of the supported error type of [`Exn`]. -pub trait Error: std::error::Error + std::any::Any + Send + Sync + 'static { - /// Raise this error as a new exception. - #[track_caller] - fn raise(self) -> Exn - where - Self: Sized, - { - Exn::new(self) - } -} - -impl Error for T where T: std::error::Error + std::any::Any + Send + Sync + 'static {} - /// Equivalent to `Ok::<_, Exn>(value)`. /// /// This simplifies creation of an `exn::Result` in places where type inference cannot deduce the @@ -119,6 +105,6 @@ impl Error for T where T: std::error::Error + std::any::Any + Send + Sync + ' /// | consider giving this pattern the explicit type `std::result::Result`, where the type parameter `E` is specified /// ``` #[expect(non_snake_case)] -pub fn Ok(value: T) -> Result { +pub fn Ok(value: T) -> Result { Result::Ok(value) } diff --git a/exn/src/macros.rs b/exn/src/macros.rs index 0778526..327cbe7 100644 --- a/exn/src/macros.rs +++ b/exn/src/macros.rs @@ -27,7 +27,7 @@ /// use std::fs; /// /// use exn::bail; -/// # fn wrapper() -> exn::Result<(), impl exn::Error> { +/// # fn wrapper() -> exn::Result<(), std::io::Error> { /// match fs::read_to_string("/path/to/file") { /// Ok(content) => println!("file contents: {content}"), /// Err(err) => bail!(err), diff --git a/exn/src/option.rs b/exn/src/option.rs index 209fd7e..e9f589c 100644 --- a/exn/src/option.rs +++ b/exn/src/option.rs @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::Error; +use std::error::Error; + use crate::Exn; use crate::Result; diff --git a/exn/src/result.rs b/exn/src/result.rs index 8d58548..8b0f5e6 100644 --- a/exn/src/result.rs +++ b/exn/src/result.rs @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::Error; +use std::error::Error; + use crate::Exn; /// A reasonable return type to use throughout an application. @@ -24,7 +25,7 @@ pub trait ResultExt { type Success; /// The `Err` type that would be wrapped in an [`Exn`]. - type Error: Error; + type Error: Error + 'static; /// Raise a new exception on the [`Exn`] inside the [`Result`]. /// @@ -37,7 +38,7 @@ pub trait ResultExt { impl ResultExt for std::result::Result where - E: Error, + E: Error + 'static, { type Success = T; type Error = E; @@ -45,7 +46,7 @@ where #[track_caller] fn or_raise(self, err: F) -> Result where - A: Error, + A: Error + 'static, F: FnOnce() -> A, { match self { @@ -65,7 +66,7 @@ where #[track_caller] fn or_raise(self, err: F) -> Result where - A: Error, + A: Error + 'static, F: FnOnce() -> A, { match self { diff --git a/exn/tests/simple.rs b/exn/tests/simple.rs index 72c9685..efde406 100644 --- a/exn/tests/simple.rs +++ b/exn/tests/simple.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use exn::Error; use exn::Exn; use exn::OptionExt; use exn::ResultExt; @@ -30,7 +29,7 @@ impl std::error::Error for SimpleError {} #[test] fn test_error_straightforward() { - let e1 = SimpleError("E1").raise(); + let e1 = Exn::new(SimpleError("E1")); let e2 = e1.raise(SimpleError("E2")); let e3 = e2.raise(SimpleError("E3")); let e4 = e3.raise(SimpleError("E4")); @@ -40,21 +39,21 @@ fn test_error_straightforward() { #[test] fn test_error_tree() { - let e1 = SimpleError("E1").raise(); + let e1 = Exn::new(SimpleError("E1")); let e3 = e1.raise(SimpleError("E3")); - let e9 = SimpleError("E9").raise(); + let e9 = Exn::new(SimpleError("E9")); let e10 = e9.raise(SimpleError("E10")); - let e11 = SimpleError("E11").raise(); + let e11 = Exn::new(SimpleError("E11")); let e12 = e11.raise(SimpleError("E12")); let e5 = Exn::from_iter([e3, e10, e12], SimpleError("E5")); - let e2 = SimpleError("E2").raise(); + let e2 = Exn::new(SimpleError("E2")); let e4 = e2.raise(SimpleError("E4")); - let e7 = SimpleError("E7").raise(); + let e7 = Exn::new(SimpleError("E7")); let e8 = e7.raise(SimpleError("E8")); let e6 = Exn::from_iter([e5, e4, e8], SimpleError("E6")); diff --git a/exn/tests/snapshots/simple__bail.snap b/exn/tests/snapshots/simple__bail.snap index c34b677..02b6606 100644 --- a/exn/tests/snapshots/simple__bail.snap +++ b/exn/tests/snapshots/simple__bail.snap @@ -2,4 +2,4 @@ source: exn/tests/simple.rs expression: result.unwrap_err() --- -An error, at exn/tests/simple.rs:92:9 +An error, at exn/tests/simple.rs:91:9 diff --git a/exn/tests/snapshots/simple__ensure_fail.snap b/exn/tests/snapshots/simple__ensure_fail.snap index e159214..ee8f606 100644 --- a/exn/tests/snapshots/simple__ensure_fail.snap +++ b/exn/tests/snapshots/simple__ensure_fail.snap @@ -2,4 +2,4 @@ source: exn/tests/simple.rs expression: result.unwrap_err() --- -An error, at exn/tests/simple.rs:112:9 +An error, at exn/tests/simple.rs:111:9 diff --git a/exn/tests/snapshots/simple__error_straightforward.snap b/exn/tests/snapshots/simple__error_straightforward.snap index e772fed..d9dadc6 100644 --- a/exn/tests/snapshots/simple__error_straightforward.snap +++ b/exn/tests/snapshots/simple__error_straightforward.snap @@ -2,12 +2,12 @@ source: exn/tests/simple.rs expression: e5 --- -E5, at exn/tests/simple.rs:37:17 +E5, at exn/tests/simple.rs:36:17 | -|-> E4, at exn/tests/simple.rs:36:17 +|-> E4, at exn/tests/simple.rs:35:17 | -|-> E3, at exn/tests/simple.rs:35:17 +|-> E3, at exn/tests/simple.rs:34:17 | -|-> E2, at exn/tests/simple.rs:34:17 +|-> E2, at exn/tests/simple.rs:33:17 | -|-> E1, at exn/tests/simple.rs:33:32 +|-> E1, at exn/tests/simple.rs:32:14 diff --git a/exn/tests/snapshots/simple__error_tree.snap b/exn/tests/snapshots/simple__error_tree.snap index 943f4ce..6e93ff9 100644 --- a/exn/tests/snapshots/simple__error_tree.snap +++ b/exn/tests/snapshots/simple__error_tree.snap @@ -2,26 +2,26 @@ source: exn/tests/simple.rs expression: e6 --- -E6, at exn/tests/simple.rs:60:14 +E6, at exn/tests/simple.rs:59:14 | -|-> E5, at exn/tests/simple.rs:52:14 +|-> E5, at exn/tests/simple.rs:51:14 | | -| |-> E3, at exn/tests/simple.rs:44:17 +| |-> E3, at exn/tests/simple.rs:43:17 | | | -| | |-> E1, at exn/tests/simple.rs:43:32 +| | |-> E1, at exn/tests/simple.rs:42:14 | | -| |-> E10, at exn/tests/simple.rs:47:18 +| |-> E10, at exn/tests/simple.rs:46:18 | | | -| | |-> E9, at exn/tests/simple.rs:46:32 +| | |-> E9, at exn/tests/simple.rs:45:14 | | -| |-> E12, at exn/tests/simple.rs:50:19 +| |-> E12, at exn/tests/simple.rs:49:19 | | -| |-> E11, at exn/tests/simple.rs:49:34 +| |-> E11, at exn/tests/simple.rs:48:15 | -|-> E4, at exn/tests/simple.rs:55:17 +|-> E4, at exn/tests/simple.rs:54:17 | | -| |-> E2, at exn/tests/simple.rs:54:32 +| |-> E2, at exn/tests/simple.rs:53:14 | -|-> E8, at exn/tests/simple.rs:58:17 +|-> E8, at exn/tests/simple.rs:57:17 | - |-> E7, at exn/tests/simple.rs:57:32 + |-> E7, at exn/tests/simple.rs:56:14 diff --git a/exn/tests/snapshots/simple__from_error.snap b/exn/tests/snapshots/simple__from_error.snap index cb43ac5..765da5f 100644 --- a/exn/tests/snapshots/simple__from_error.snap +++ b/exn/tests/snapshots/simple__from_error.snap @@ -2,4 +2,4 @@ source: exn/tests/simple.rs expression: result.unwrap_err() --- -An error, at exn/tests/simple.rs:81:9 +An error, at exn/tests/simple.rs:80:9 diff --git a/exn/tests/snapshots/simple__option_ext.snap b/exn/tests/snapshots/simple__option_ext.snap index d5d3390..8834b0d 100644 --- a/exn/tests/snapshots/simple__option_ext.snap +++ b/exn/tests/snapshots/simple__option_ext.snap @@ -2,4 +2,4 @@ source: exn/tests/simple.rs expression: result.unwrap_err() --- -An error, at exn/tests/simple.rs:74:25 +An error, at exn/tests/simple.rs:73:25 diff --git a/exn/tests/snapshots/simple__result_ext.snap b/exn/tests/snapshots/simple__result_ext.snap index 013566e..85b189b 100644 --- a/exn/tests/snapshots/simple__result_ext.snap +++ b/exn/tests/snapshots/simple__result_ext.snap @@ -2,6 +2,6 @@ source: exn/tests/simple.rs expression: result.unwrap_err() --- -Another error, at exn/tests/simple.rs:67:25 +Another error, at exn/tests/simple.rs:66:25 | -|-> An error, at exn/tests/simple.rs:67:25 +|-> An error, at exn/tests/simple.rs:66:25