Skip to main content

error2/
lib.rs

1//! Comprehensive error handling library with detailed backtrace tracking.
2//!
3//! `error2` provides enhanced error handling capabilities for Rust applications,
4//! focusing on detailed error propagation tracking and ergonomic error conversion.
5//!
6//! # Features
7//!
8//! - **Backtrace Tracking** - Automatically capture error creation location; manually record propagation with `.attach()`
9//! - **Error Chaining** - Chain errors from different libraries while preserving context
10//! - **Derive Macro** - `#[derive(Error2)]` for easy error type creation
11//! - **Type Conversion** - `Result<T, E1> -> Result<T, E2>`, `Option<T> -> Result<T, E>` with `.context()`
12//! - **Type Erasure** - `BoxedError2` for anyhow-like ergonomics
13//!
14//! # Quick Start
15//!
16//! Add to your `Cargo.toml`:
17//!
18//! ```toml
19//! [dependencies]
20//! error2 = "0.13.2"
21//! ```
22//!
23//! Define your error types:
24//!
25//! ```
26//! use std::io;
27//!
28//! use error2::prelude::*;
29//!
30//! #[derive(Debug, Error2)]
31//! pub enum MyError {
32//!     #[error2(display("IO error: {source}"))]
33//!     Io {
34//!         source: io::Error,
35//!         backtrace: Backtrace,
36//!     },
37//!
38//!     #[error2(display("not found: {key}"))]
39//!     NotFound { key: String, backtrace: Backtrace },
40//! }
41//! ```
42//!
43//! Use in your functions:
44//!
45//! ```
46//! # use error2::prelude::*;
47//! # use std::io;
48//! # #[derive(Debug, Error2)]
49//! # pub enum MyError {
50//! #     #[error2(display("IO error"))]
51//! #     Io { source: io::Error, backtrace: Backtrace },
52//! #     #[error2(display("not found: {key}"))]
53//! #     NotFound { key: String, backtrace: Backtrace },
54//! # }
55//! fn read_config(path: &str) -> Result<String, MyError> {
56//!     // Convert io::Error to MyError::Io
57//!     let content = std::fs::read_to_string(path).context(Io2)?;
58//!
59//!     // Convert Option to Result
60//!     let value = content
61//!         .lines()
62//!         .next()
63//!         .context(NotFound2 { key: "first line" })?;
64//!
65//!     Ok(value.to_string())
66//! }
67//! ```
68//!
69//! # Three Error Patterns
70//!
71//! Error2 supports three types of errors based on their field structure:
72//!
73//! ## 1. Root Error (New Error Origin)
74//!
75//! Use when creating a new error (not wrapping another):
76//!
77//! ```
78//! # use error2::prelude::*;
79//! #[derive(Debug, Error2)]
80//! pub enum AppError {
81//!     #[error2(display("invalid ID: {id}"))]
82//!     InvalidId {
83//!         id: i64,
84//!         backtrace: Backtrace, // Only backtrace, no source
85//!     },
86//! }
87//! ```
88//!
89//! ## 2. Std Error (Wrapping std::error::Error)
90//!
91//! Use when wrapping standard library or third-party errors:
92//!
93//! ```
94//! # use error2::prelude::*;
95//! # use std::io;
96//! #[derive(Debug, Error2)]
97//! pub enum AppError {
98//!     #[error2(display("file error"))]
99//!     FileError {
100//!         source: io::Error,    // Wrapped error
101//!         backtrace: Backtrace, // New backtrace
102//!     },
103//! }
104//! ```
105//!
106//! ## 3. Error2 Error (Chaining Error2 Types)
107//!
108//! Use when wrapping another Error2 type (reuses backtrace):
109//!
110//! ```
111//! # use error2::prelude::*;
112//! # #[derive(Debug, Error2)]
113//! # #[error2(display("config error"))]
114//! # pub struct ConfigError { backtrace: Backtrace }
115//! #[derive(Debug, Error2)]
116//! pub enum AppError {
117//!     #[error2(display("configuration failed"))]
118//!     Config {
119//!         source: ConfigError, // Only source, backtrace reused
120//!     },
121//! }
122//! ```
123//!
124//! # Core Traits
125//!
126//! - [`Error2`] - Extends `std::error::Error` with backtrace support
127//! - [`Context`] - Type conversion: `Result<T, Source> -> Result<T, Target>`, `Option<T> -> Result<T, E>`
128//! - [`Attach`] - Record error propagation locations
129//! - [`RootError`] - Convenience methods for creating root errors
130//!
131//! # Type Erasure
132//!
133//! [`BoxedError2`] provides anyhow-like ergonomics:
134//!
135//! ```
136//! use error2::prelude::*;
137//!
138//! fn do_something() -> Result<(), BoxedError2> {
139//!     std::fs::read_to_string("file.txt").context(ViaStd)?; // Convert to BoxedError2
140//!     Ok(())
141//! }
142//! ```
143//!
144//! # Location Tracking
145//!
146//! Use `.attach()` to record error propagation:
147//!
148//! ```
149//! # use error2::prelude::*;
150//! # use std::io;
151//! # #[derive(Debug, Error2)]
152//! # #[error2(display("error"))]
153//! # struct MyError { source: io::Error, backtrace: Backtrace }
154//! # fn inner() -> Result<(), MyError> { Ok(()) }
155//! fn outer() -> Result<(), MyError> {
156//!     let result = inner().attach()?; // Records this location
157//!     Ok(result)
158//! }
159//! ```
160//!
161//! The backtrace shows multiple locations:
162//!
163//! ```
164//! # use error2::prelude::*;
165//! # use std::io;
166//! # #[derive(Debug, Error2)]
167//! # #[error2(display("error"))]
168//! # struct MyError { source: io::Error, backtrace: Backtrace }
169//! # fn inner() -> Result<(), MyError> {
170//! #     let err = io::Error::new(io::ErrorKind::NotFound, "not found");
171//! #     Err(err).context(MyError2)
172//! # }
173//! # fn outer() -> Result<(), MyError> { inner().attach() }
174//! # fn main() {
175//! use regex::Regex;
176//!
177//! if let Err(e) = outer() {
178//!     let msg = e.backtrace().error_message();
179//!
180//!     // Full error format with multiple locations:
181//!     // MyError: error
182//!     //     at /path/to/file.rs:496:14
183//!     //     at /path/to/file.rs:498:45
184//!     // std::io::error::Error: not found
185//!
186//!     let re = Regex::new(concat!(
187//!         r"(?s)^.+MyError: error",
188//!         r"\n    at .+\.rs:\d+:\d+",
189//!         r"\n    at .+\.rs:\d+:\d+",
190//!         r"\nstd::io::error::Error: not found$",
191//!     ))
192//!     .unwrap();
193//!     assert!(re.is_match(msg.as_ref()));
194//! }
195//! # }
196//! ```
197
198#![cfg_attr(docsrs, feature(doc_cfg))]
199
200mod _attach;
201mod backtrace;
202mod boxed;
203mod context;
204mod error2;
205mod extract;
206mod location;
207mod macros;
208mod root_error;
209mod str_id;
210
211/// Attach adapters for iterators, futures, and streams.
212pub mod attach;
213/// Error kind enum for downcasting [`BoxedError2`].
214///
215/// See [`ErrorKind`](kind::ErrorKind) for details.
216pub mod kind;
217/// Internal transformation traits (not for direct use).
218pub mod transform;
219
220/// Re-exports of commonly used types and traits.
221///
222/// Import with `use error2::prelude::*;` to get:
223/// - [`Error2`] trait
224/// - [`Context`], [`Attach`], [`RootError`] traits
225/// - [`Backtrace`], [`BoxedError2`] types
226/// - [`ViaRoot`], [`ViaStd`], [`ViaErr2`] wrappers
227/// - `#[derive(Error2)]` macro (if `derive` feature enabled)
228pub mod prelude {
229    #[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
230    #[cfg(feature = "derive")]
231    pub use ::error2_derive::Error2;
232
233    // traits
234    pub use crate::{Attach as _, Context as _, RootError as _, error2::Error2};
235    // types
236    pub use crate::{Backtrace, BoxedError2, ViaErr2, ViaRoot, ViaStd};
237}
238
239#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
240#[cfg(feature = "derive")]
241pub use ::error2_derive::Error2;
242
243pub use self::{
244    _attach::Attach,
245    backtrace::Backtrace,
246    boxed::{BoxedError2, ViaErr2, ViaRoot, ViaStd},
247    context::Context,
248    error2::Error2,
249    location::Location,
250    root_error::RootError,
251};
252pub(crate) use self::{backtrace::BakctraceEntry, extract::extract_error_message, str_id::StrId};
253
254pub(crate) mod private {
255    #[derive(Debug, Clone, Copy)]
256    pub enum ViaPartial {}
257
258    #[derive(Debug, Clone, Copy)]
259    pub enum ViaFull {}
260}
261
262#[doc(hidden)]
263pub fn push_error<E: Error2 + ?Sized>(error: &mut E, location: Location) {
264    let display = error.to_string();
265    let backtrace = error.backtrace_mut();
266    let type_name = core::any::type_name::<E>();
267
268    backtrace.push_error(type_name, display, location);
269}