error_ext/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3//! Error utilities.
4
5#[cfg(feature = "axum")]
6#[cfg_attr(docsrs, doc(cfg(feature = "axum")))]
7pub mod axum;
8
9use std::error::Error as StdError;
10
11/// Convenience for `async` and `anyhow` friendly dynamic error
12/// `Box<dyn std::error::Error + Send + Sync + 'static>`.
13pub type BoxError = Box<dyn StdError + Send + Sync + 'static>;
14
15/// Extension methods for std errors.
16pub trait StdErrorExt
17where
18    Self: StdError,
19{
20    /// Format this error as a chain of colon separated strings built from this error and all
21    /// recursive sources.
22    ///
23    /// Can be used to log errors like this:
24    ///
25    /// `error!(error = error.as_chain(), "cannot do this or that");`
26    fn as_chain(&self) -> String {
27        let mut sources = vec![];
28        sources.push(self.to_string());
29
30        let mut source = self.source();
31        while let Some(s) = source {
32            sources.push(s.to_string());
33            source = s.source();
34        }
35
36        sources.join(": ")
37    }
38
39    /// Converts this error – given it implements `Sized` + `Send` +`Sync` and `'static` – into a
40    /// [BoxError].
41    fn into_boxed(self) -> BoxError
42    where
43        Self: Sized + Send + Sync + 'static,
44    {
45        self.into()
46    }
47}
48
49impl<T> StdErrorExt for T where T: StdError {}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54    use thiserror::Error;
55
56    #[derive(Debug, Error)]
57    #[error("outer")]
58    struct Outer(#[source] Inner);
59
60    #[derive(Debug, Error)]
61    #[error("inner")]
62    struct Inner;
63
64    #[test]
65    fn test_as_chain() {
66        let error = Outer(Inner);
67        assert_eq!(error.as_chain(), "outer: inner");
68    }
69
70    #[test]
71    fn test_into_boxed() {
72        let error = Outer(Inner);
73        let error = error.into_boxed();
74        assert_eq!(error.to_string(), "outer");
75    }
76}