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;