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}