exn/
lib.rs

1// Copyright 2025 FastLabs Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A context-aware concrete Error type built on `std::error::Error`
16//!
17//! # Examples
18//!
19//! ```
20//! use exn::Result;
21//! use exn::ResultExt;
22//! use exn::bail;
23//!
24//! // It's recommended to define errors as structs. Exn will maintain the error tree automatically.
25//! #[derive(Debug)]
26//! struct LogicError(String);
27//!
28//! impl std::fmt::Display for LogicError {
29//!     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30//!         write!(f, "logic error: {}", self.0)
31//!     }
32//! }
33//!
34//! impl std::error::Error for LogicError {}
35//!
36//! fn do_logic() -> Result<(), LogicError> {
37//!     bail!(LogicError("0 == 1".to_string()));
38//! }
39//!
40//! // Errors can be enum but notably don't need to chain source error.
41//! #[derive(Debug)]
42//! enum AppError {
43//!     Fatal { consequences: &'static str },
44//!     Trivial,
45//! }
46//!
47//! impl std::fmt::Display for AppError {
48//!     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49//!         match self {
50//!             AppError::Fatal { consequences } => write!(f, "fatal error: {consequences}"),
51//!             AppError::Trivial => write!(f, "trivial error"),
52//!         }
53//!     }
54//! }
55//!
56//! impl std::error::Error for AppError {}
57//!
58//! fn main() {
59//!     if let Err(err) = do_logic().or_raise(|| AppError::Fatal {
60//!         consequences: "math no longer works",
61//!     }) {
62//!         eprintln!("{err:?}");
63//!     }
64//! }
65//! ```
66//!
67//! The above program will print an error message like:
68//!
69//! ```text
70//! fatal error: math no longer works, at exn/src/lib.rs:44:16
71//! |
72//! |-> logic error: 0 == 1, at exn/src/lib.rs:40:5
73//! ```
74
75#![cfg_attr(docsrs, feature(doc_cfg))]
76#![deny(missing_docs)]
77
78mod debug;
79mod display;
80mod impls;
81mod macros;
82mod option;
83mod result;
84
85pub use self::impls::Exn;
86pub use self::impls::Frame;
87pub use self::option::OptionExt;
88pub use self::result::Result;
89pub use self::result::ResultExt;
90
91/// A trait bound of the supported error type of [`Exn`].
92pub trait Error: std::error::Error + std::any::Any + Send + Sync + 'static {
93    /// Raise this error as a new exception.
94    #[track_caller]
95    fn raise(self) -> Exn<Self>
96    where
97        Self: Sized,
98    {
99        Exn::new(self)
100    }
101}
102
103impl<T> Error for T where T: std::error::Error + std::any::Any + Send + Sync + 'static {}
104
105/// Equivalent to `Ok::<_, Exn<E>>(value)`.
106///
107/// This simplifies creation of an `exn::Result` in places where type inference cannot deduce the
108/// `E` type of the result &mdash; without needing to write `Ok::<_, Exn<E>>(value)`.
109///
110/// One might think that `exn::Result::Ok(value)` would work in such cases, but it does not.
111///
112/// ```console
113/// error[E0282]: type annotations needed for `std::result::Result<i32, E>`
114///   --> src/main.rs:11:13
115///    |
116/// 11 |     let _ = exn::Result::Ok(1);
117///    |         -   ^^^^^^^^^^^^^^^ cannot infer type for type parameter `E` declared on the enum `Result`
118///    |         |
119///    |         consider giving this pattern the explicit type `std::result::Result<i32, E>`, where the type parameter `E` is specified
120/// ```
121#[expect(non_snake_case)]
122pub fn Ok<T, E: Error>(value: T) -> Result<T, E> {
123    Result::Ok(value)
124}