source_chain/
lib.rs

1//! # source-chain
2//!
3//! Formats StdError with it's source chain
4//!
5//! ```rust
6//! #[derive(Debug, thiserror::Error)]
7//! enum Error {
8//!     #[error("unknown file {1}")]
9//!     UnknownFile(#[source] std::io::Error, &'static str),
10//! }
11//!
12//! fn file_error() -> Result<String, Error> {
13//!     let filename = "unknown-file.txt";
14//!     std::fs::read_to_string(filename).map_err(|e| Error::UnknownFile(e, filename))
15//! }
16//!
17//! let err = file_error().unwrap_err();
18//! assert_eq!(
19//!     source_chain::to_string(&err),
20//!     "unknown file unknown-file.txt\nCaused by:\n\tNo such file or directory (os error 2)"
21//! );
22//!
23//! let dyn_err: Box<dyn std::error::Error> = Box::new(err);
24//! assert_eq!(
25//!     // notice dereferencing
26//!     source_chain::to_string(&*dyn_err),
27//!     "unknown file unknown-file.txt\nCaused by:\n\tNo such file or directory (os error 2)"
28//! );
29//! ```
30
31#![warn(clippy::all, missing_docs, nonstandard_style, future_incompatible)]
32
33/// A helper function to format an error with its source chain.
34///
35/// This function works with both `&Error` and `Box<dyn Error>`. When passing a boxed error,
36/// make sure to dereference it using `&*e`.
37pub fn to_string(e: &(dyn std::error::Error + 'static)) -> String {
38    use std::fmt::Write as _;
39
40    let mut s = e.to_string();
41    let mut current = e.source();
42    if current.is_some() {
43        s.push_str("\nCaused by:");
44    }
45    while let Some(cause) = current {
46        write!(s, "\n\t{}", cause).ok();
47        current = cause.source();
48    }
49    s
50}