anyerr/
lib.rs

1//! `anyerr` is a comprehensive error handling library designed to offer
2//! flexibility, extensibility, and an ergonomic way to handle errors in Rust
3//! applications.
4//!
5//! This library provides a central [`AnyError`] type that can carry arbitrary
6//! error information, including a custom error kind, a backtrace, contextual
7//! data and so on. It enables developers to create error types composing
8//! different levels of errors without sacrificing the ability to preserve rich
9//! context information.
10//!
11//! ## Key Features
12//!
13//! - **Error Composition**: Wrap and combine errors while preserving their
14//!   original information and access the underlying errors if needed.
15//! - **Customizable Error Kind**: Make use of predefined error kinds offered
16//!   by this crate or define your own error kinds by implementing the [`Kind`]
17//!   trait.
18//! - **Contextual Data**: Attach rich context information to errors using
19//!   different pluggable context types.
20//! - **Backtrace Support**: Automatically captures backtraces for easier
21//!   debugging.
22//! - **Error Reporting**: Customize and write formated and detailed error
23//!   messages to `stdout`, loggers and so on.
24//!
25//! ## Getting Started
26//!
27//! ### Defining a Custom Error Type
28//!
29//! [`AnyError`] is the core of this crate. It works in a way resembled to
30//! [`Box<dyn Error>`], by implementing the [`Error`] trait and leverage the
31//! functionality of the [`Any`] trait, while it's also [`Send`] and [`Sync`],
32//! allowing safely accesses across multiple concurrent threads. [`AnyError`]
33//! is easy to get started with, though, it's not somthing like
34//! [`Box<dyn Error>`] that can be used directly in your codebase, but a highly
35//! customizable type requiring you to make decisions about its components.
36//!
37//! An [`AnyError<C, K>`] has two generic type parameters `C` and `K`, stand
38//! for the context storage and the error kind respectively.
39//!
40//! [`AbstractContext`] is implemented for `C`, so is [`Context`] usually but
41//! it's not required. With `C` implementing [`Context`], you can attach
42//! additional contextual data to an [`AnyError`] for better debugging. An
43//! example of one of the most useful contexts is
44//! [`LiteralKeyStringMapContext`], which holds entries of a `&'static str`
45//! and `String` pair structure, and stores the the [`Debug`] representation
46//! of values.
47//!
48//! `K` is required to implement the trait [`Kind`], specifying a general kind
49//! of the error. Although a structured error handling style is not preferred
50//! under this circumstance, an error kind enables more fine-grained logging
51//! and tracing or enhances experience of other aspects. [`DefaultErrorKind`]
52//! is a [`Kind`] provided by this crate, and the design of its variant is
53//! based on the author's web backend developemnt experience.
54//!
55//! Once you have chosen the components you need, you can define your custom
56//! error type, by supplying [`AnyError`] with the selected context and error
57//! kind. Here's an example:
58//!
59//! ```rust
60//! // Make this module accessible to your whole crate.
61//! mod err {
62//!     use anyerr::AnyError as AnyErrorTemplate;
63//!     use anyerr::context::LiteralKeyStringMapContext;
64//!
65//!     pub use anyerr::{Intermediate, Overlay}; // These are helper traits.
66//!     pub use anyerr::kind::DefaultErrorKind as ErrKind;
67//!     pub use anyerr::Report;
68//!
69//!     pub type AnyError = AnyErrorTemplate<LiteralKeyStringMapContext, ErrKind>;
70//!     pub type AnyResult<T> = Result<T, AnyError>;
71//! }
72//! // Include this in wherever you need `AnyError`.
73//! use err::*;
74//! ```
75//!
76//! ### Creating and Using Errors
77//!
78//! Here's how to create [`AnyError`] in your application:
79//!
80//! ```rust
81//! # mod err {
82//! #     use anyerr::AnyError as AnyErrorTemplate;
83//! #     use anyerr::context::LiteralKeyStringMapContext;
84//! #
85//! #     pub use anyerr::{Intermediate, Overlay};
86//! #     pub use anyerr::kind::DefaultErrorKind as ErrKind;
87//! #     pub use anyerr::Report;
88//! #
89//! #     pub type AnyError = AnyErrorTemplate<LiteralKeyStringMapContext, ErrKind>;
90//! #     pub type AnyResult<T> = Result<T, AnyError>;
91//! # }
92//! use err::*;
93//!
94//! fn fail() -> AnyResult<()> {
95//!     // Use `AnyError::minimal()` to create a simple [`String`]-based error.
96//!     Err(AnyError::minimal("this function always fails"))
97//! }
98//!
99//! fn check_positive(x: i32) -> AnyResult<()> {
100//!     if x > 0 {
101//!         return Ok(());
102//!     }
103//!     // Use `AnyError::quick()` to quickly create an error with an error
104//!     // message and an error kind.
105//!     Err(AnyError::quick(
106//!         "expects `x` to be a positive number",
107//!         ErrKind::ValueValidation
108//!     ))
109//! }
110//!
111//! fn try_add_username(
112//!     usernames: &mut Vec<String>,
113//!     new_username: String
114//! ) -> AnyResult<usize> {
115//!     let res = usernames.iter()
116//!         .enumerate()
117//!         .find(|(_, username)| **username == new_username)
118//!         .map(|(index, _)| index);
119//!     if let Some(index) = res {
120//!         // Use `AnyError::builder()` to create an error with all essential
121//!         // context you'll need.
122//!         let err = AnyError::builder()
123//!             .message("the username already exists")
124//!             .kind(ErrKind::RuleViolation)
125//!             .context("new_username", new_username)
126//!             .context("index", index)
127//!             .build();
128//!         Err(err)
129//!     } else {
130//!         usernames.push(new_username);
131//!         Ok(usernames.len() - 1)
132//!     }
133//! }
134//!
135//! fn parse_i32(input: &str) -> AnyResult<i32> {
136//!     // Use `AnyError::wrap()` to wrap any other error type.
137//!     input.parse::<i32>().map_err(AnyError::wrap)
138//! }
139//! ```
140//!
141//! Let's take the third function `try_add_username()` as an example to
142//! demonstrate how we can use [`AnyError`]:
143//!
144//! ```rust
145//! # mod err {
146//! #     use anyerr::AnyError as AnyErrorTemplate;
147//! #     use anyerr::context::LiteralKeyStringMapContext;
148//! #
149//! #     pub use anyerr::{Intermediate, Overlay};
150//! #     pub use anyerr::kind::DefaultErrorKind as ErrKind;
151//! #     pub use anyerr::Report;
152//! #
153//! #     pub type AnyError = AnyErrorTemplate<LiteralKeyStringMapContext, ErrKind>;
154//! #     pub type AnyResult<T> = Result<T, AnyError>;
155//! # }
156//! #
157//! use err::*;
158//! #
159//! # fn try_add_username<S: Into<String>>(
160//! #     usernames: &mut Vec<String>,
161//! #     new_username: S
162//! # ) -> AnyResult<usize> {
163//! #     let new_username = new_username.into();
164//! #     let res = usernames.iter()
165//! #         .enumerate()
166//! #         .find(|(_, username)| **username == new_username)
167//! #         .map(|(index, _)| index);
168//! #     if let Some(index) = res {
169//! #         // Use `AnyError::builder()` to create an error with all essential
170//! #         // context you'll need.
171//! #         let err = AnyError::builder()
172//! #             .message("the username already exists")
173//! #             .kind(ErrKind::RuleViolation)
174//! #             .context("new_username", new_username)
175//! #             .context("index", index)
176//! #             .build();
177//! #         Err(err)
178//! #     } else {
179//! #         usernames.push(new_username);
180//! #         Ok(usernames.len() - 1)
181//! #     }
182//! # }
183//!
184//! fn main() {
185//!     let mut usernames = Vec::new();
186//!
187//!     let res = try_add_username(&mut usernames, "foo").unwrap();
188//!     assert_eq!(res, 0);
189//!
190//!     let err = try_add_username(&mut usernames, "foo").unwrap_err();
191//!     assert_eq!(err.to_string(), "the username already exists"); // Or `err.message()`.
192//!     assert_eq!(err.kind(), ErrKind::RuleViolation);
193//!     assert_eq!(err.get("new_username"), Some("\"foo\""));
194//!     assert_eq!(err.get("index"), Some("0"));
195//! }
196//! ```
197//!
198//! ### Error Wrapping and Chaining
199//!
200//! The `AnyError` type supports convenient error wrapping, allowing you to
201//! maintain the original error while adding additional context. Methods in
202//! the [`Overlay`] and [`Intermediate`] helper traits provides ergonomic
203//! means for you to make an overlay of your existing error and attach rich
204//! context to it.
205//!
206//! Say we'd like to reteive a `User` entity by its username from the
207//! `UserRepository`. It's acknowledged that the query may fails due to a
208//! variety of reasons, but we don't care about the details but whether we
209//! could get that entity. The following codeblock demonstrates this idea.
210//!
211//! ```no_run,rust
212//! # mod err {
213//! #     use anyerr::AnyError as AnyErrorTemplate;
214//! #     use anyerr::context::LiteralKeyStringMapContext;
215//! #
216//! #     pub use anyerr::{Intermediate, Overlay};
217//! #     pub use anyerr::kind::DefaultErrorKind as ErrKind;
218//! #     pub use anyerr::Report;
219//! #
220//! #     pub type AnyError = AnyErrorTemplate<LiteralKeyStringMapContext, ErrKind>;
221//! #     pub type AnyResult<T> = Result<T, AnyError>;
222//! # }
223//! #
224//! # use std::sync::Arc;
225//! use err::*;
226//! #
227//! # struct User;
228//! #
229//! # struct Data;
230//! #
231//! # type DeserializationError = AnyError;
232//! #
233//! # impl TryFrom<Data> for User {
234//! #     type Error = DeserializationError;
235//! #
236//! #     fn try_from(_data: Data) -> Result<User, Self::Error> {
237//! #         Err(DeserializationError::minimal("Could not deserialize a `User` from the given data"))
238//! #     }
239//! # }
240//! # struct Connection;
241//! #
242//! # type DbError = AnyError;
243//! #
244//! # impl Connection {
245//! #     fn query(&self, statement: &str) -> Result<Data, DbError> {
246//! #         Err(DbError::minimal("could not run the SQL query"))
247//! #     }
248//! # }
249//!
250//! struct UserRepository {
251//!     conn: Arc<Connection>,
252//! }
253//!
254//! impl UserRepository {
255//!     pub fn find_by_username(&self, username: &str) -> AnyResult<User> {
256//!         // Don't build SQL statements yourself in practice.
257//!         let statement = format!("SELECT * FROM users WHERE users.username = '{username}'");
258//!         let data = self.conn.query(&statement)
259//!             .overlay(("could not get a `User` due to SQL execution error", ErrKind::EntityAbsence))
260//!             .context("username", username)
261//!             .context("statement", statement)?;
262//!         let entity = User::try_from(data)
263//!             .overlay(("could not get a `User` due to serialization error", ErrKind::EntityAbsence))
264//!             .context("username", username)?;
265//!         Ok(entity)
266//!     }
267//! }
268//! ```
269//!
270//! ### Error Reporting
271//!
272//! You might have the experience that you wrote the code which iterated over
273//! the error chain and formated causes. It's pretty tedious to manually and
274//! repeatly write such code. Therefore, this crate does this for you by
275//! providing [`Report`]. [`Report`] captures your function's result and then
276//! you can output the error report directly to terminals, loggers or whatever.
277//!
278//! ```rust
279//! # mod err {
280//! #     use anyerr::AnyError as AnyErrorTemplate;
281//! #     use anyerr::context::LiteralKeyStringMapContext;
282//! #
283//! #     pub use anyerr::{Intermediate, Overlay};
284//! #     pub use anyerr::kind::DefaultErrorKind as ErrKind;
285//! #     pub use anyerr::Report;
286//! #
287//! #     pub type AnyError = AnyErrorTemplate<LiteralKeyStringMapContext, ErrKind>;
288//! #     pub type AnyResult<T> = Result<T, AnyError>;
289//! # }
290//! #
291//! use err::*;
292//!
293//! fn source_error() -> AnyResult<()> {
294//!     let err = AnyError::builder()
295//!         .message("the source error is here")
296//!         .kind(ErrKind::InfrastructureFailure)
297//!         .context("key1", "value1")
298//!         .context("key2", "value2")
299//!         .build();
300//!     Err(err)
301//! }
302//!
303//! fn intermediate_error() -> AnyResult<()> {
304//!     source_error()
305//!         .overlay("the intermediate error is here")
306//!         .context("key3", "value3")?;
307//!     Ok(())
308//! }
309//!
310//! fn toplevel_error() -> AnyResult<()> {
311//!     intermediate_error()
312//!         .overlay("the toplevel error is here")?;
313//!     Ok(())
314//! }
315//!
316//! let report1 = Report::wrap(toplevel_error().unwrap_err()).pretty(false);
317//! let report2 = Report::capture(|| -> AnyResult<()> { toplevel_error() });
318//! println!("Error: {report1}");
319//! println!("{report2}");
320//! ```
321//!
322//! The output of `report1`:
323//!
324//! ```ignored,plain
325//! Error: (Unknown) the toplevel error is here: (Unknown) the intermediate error is here: (InfrastructureFailure) the source error is here [key3 = "value3", key1 = "value1", key2 = "value2"]
326//! ```
327//!
328//! The output of `report2`:
329//!
330//! ```ignored,plain
331//! Error:
332//!     (InfrastructureFailure) the toplevel error is here
333//! Caused by:
334//!     (Unknown) the intermediate error is here
335//!     [key3 = "value3"]
336//! Caused by:
337//!     (Unknown) the source error is here
338//!     [key1 = "value1", key2 = "value2"]
339//!
340//! Stack backtrace:
341//!   0: anyerr::core::data::ErrorDataBuilder<C,K>::build
342//!             at ./src/core/data.rs:210:28
343//!   1: anyerr::core::AnyErrorBuilder<C,K>::build
344//!             at ./src/core.rs:415:24
345//!   2: anyerr::source_error
346//!             at ./src/main.rs:18:15
347//!   3: anyerr::intermediate_error
348//!             at ./src/main.rs:28:5
349//!   4: anyerr::toplevel_error
350//!             at ./src/main.rs:35:5
351//!   5: anyerr::main::{{closure}}
352//!             at ./src/main.rs:40:43
353//!   6: anyerr::report::Report<C,K>::capture
354//!             at ./src/report.rs:52:15
355//!   7: anyerr::main
356//!             at ./src/main.rs:40:5
357//!    ...
358//! ```
359//!
360//! Using [`Report`] in `main()`'s returning position is also allowed:
361//!
362//! ```no_run,rust
363//! # mod err {
364//! #     use anyerr::AnyError as AnyErrorTemplate;
365//! #     use anyerr::context::LiteralKeyStringMapContext;
366//! #
367//! #     pub use anyerr::{Intermediate, Overlay};
368//! #     pub use anyerr::kind::DefaultErrorKind as ErrKind;
369//! #     pub use anyerr::Report;
370//! #
371//! #     pub type AnyError = AnyErrorTemplate<LiteralKeyStringMapContext, ErrKind>;
372//! #     pub type AnyResult<T> = Result<T, AnyError>;
373//! # }
374//! #
375//! use std::process::Termination;
376//! use err::*;
377//! #
378//! #  fn source_error() -> AnyResult<()> {
379//! #     let err = AnyError::builder()
380//! #         .message("the source error is here")
381//! #         .kind(ErrKind::InfrastructureFailure)
382//! #         .context("key1", "value1")
383//! #         .context("key2", "value2")
384//! #         .build();
385//! #     Err(err)
386//! # }
387//! #
388//! # fn intermediate_error() -> AnyResult<()> {
389//! #     source_error()
390//! #         .overlay("the intermediate error is here")
391//! #         .context("key3", "value3")?;
392//! #     Ok(())
393//! # }
394//! #
395//! # fn toplevel_error() -> AnyResult<()> {
396//! #     intermediate_error()
397//! #         .overlay("the toplevel error is here")?;
398//! #     Ok(())
399//! # }
400//!
401//! fn main() -> impl Termination {
402//!     Report::capture(|| {
403//!         toplevel_error()?;
404//!         Ok(())
405//!     })
406//! }
407//! ```
408//!
409//! For more information about error reporting customization, see the
410//! documentations of [`Report`].
411//!
412//! ## Advanced Usage
413//!
414//! ### Different Context Types
415//!
416//! This crate allows using different context types, such as
417//! [`SingletonContext`], [`StringContext`], [`AnyContext`] or the ones you
418//! developed by yourself, depending on how you want to manage and retrieve
419//! additional information from your errors. It's even viable that you don't
420//! want your error type to carry a context storage, through the [`NoContext`]
421//! trait. Each context type offers unique capabilities for structuring error
422//! metadata.
423//!
424//! For more information, refer to the types in the [`crate::context`] module.
425//!
426//! ### Usage without an Error Kind
427//!
428//! For some reasons, you may not want each error to have an error kind. This
429//! crate offers you [`NoErrorKind`], which actually has only one variant as
430//! its default value. By selecting [`NoErrorKind`], you no longer need to
431//! do anything with error kinds.
432//!
433//! [`Any`]: std::any::Any
434//! [`AbstractContext`]: crate::context::AbstractContext
435//! [`Context`]: crate::context::Context
436//! [`Debug`]: std::fmt::Debug
437//! [`DefaultErrorKind`]: crate::kind::DefaultErrorKind
438//! [`Error`]: std::error::Error
439//! [`Kind`]: crate::kind::Kind
440//! [`LiteralKeyStringMapContext`]: crate::context::LiteralKeyStringMapContext
441//! [`NoContext`]: crate::context::NoContext
442//! [`NoErrorKind`]: crate::kind::NoErrorKind
443//! [`SingletonContext`]: crate::context::SingletonContext
444//! [`StringContext`]: crate::context::StringContext
445//! [`AnyContext`]: crate::context::AnyContext
446
447pub mod context;
448pub mod converter;
449pub mod core;
450pub mod kind;
451pub mod overlay;
452pub mod report;
453
454pub use core::AnyError;
455pub use overlay::{Intermediate, Overlay};
456pub use report::Report;