pretty_error_debug/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! # pretty-error-debug
4//!
5//! [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/Kijewski/pretty-error-debug/ci.yml?branch=main&logo=github&style=flat-square)](https://github.com/Kijewski/pretty-error-debug/actions/workflows/ci.yml)
6//! [![Crates.io](https://img.shields.io/crates/v/pretty-error-debug?logo=rust&style=flat-square)](https://crates.io/crates/pretty-error-debug)
7//! ![Minimum supported Rust version: 1.56](https://img.shields.io/badge/rustc-1.56+-important?logo=rust&style=flat-square "Minimum Supported Rust Version: 1.56")
8//! [![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-informational?logo=apache&style=flat-square)](/LICENSE-MIT "License: MIT OR Apache-2.0")
9//!
10//! Display a the chain of an error. Most useful as [`Result<(), E>`](std::result::Result) for your `fn main()`,
11//! and in conjunction with [`thiserror`](https://crates.io/crates/thiserror).
12//!
13//! This crate simply <del>plagiarized</del> <ins>extracted</ins> all the relevant formatting code from
14//! [`anyhow`](https://crates.io/crates/anyhow).
15//!
16//! ## Example message
17//!
18//! ```text
19//! Error: Got a 'middle' error
20//!
21//! Caused by:
22//!     1: A nested error occured
23//!     2: 'inner' failed
24//!     3: Caught an error: Not implemented, yet.
25//! ```
26//!
27//! ## With `thiserror`
28//!
29//! ```rust,ignore
30//! #[derive(pretty_error_debug::Debug, thiserror::Error)]
31//! pub enum MyError {
32//!     #[error("Error variant 1 happened")]
33//!     Variant1(#[from] Error1),
34//!     #[error("Error variant 2 happened")]
35//!     Variant2(#[from] Error2),
36//! }
37//!
38//! fn main() -> Result<(), MyError> {
39//! # /*
40//!     ...
41//! # */ Ok(())
42//! }
43//! ```
44//!
45//! ## With `thiserror`, but without a new type
46//!
47//! ```rust,ignore
48//! #[derive(Debug, thiserror::Error)]
49//! pub enum MyError {
50//!     #[error("Error variant 1 happened")]
51//!     Variant1(#[from] Error1),
52//!     #[error("Error variant 2 happened")]
53//!     Variant2(#[from] Error2),
54//! }
55//!
56//! fn main() -> Result<(), pretty_error_debug::Wrapper<MyError>> {
57//! # /*
58//!     ...
59//! # */ Ok(())
60//! }
61//! ```
62//!
63//! ## Without `thiserror`
64//!
65//! ```rust
66//! use std::error::Error;
67//! use std::fmt::{self, Write};
68//! #
69//! # #[derive(Debug)] pub struct Error1;
70//! # impl Error for Error1 {}
71//! # impl fmt::Display for Error1 {
72//! #    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73//! #        write!(f, "Error1")
74//! #    }
75//! # }
76//! #
77//! # #[derive(Debug)] pub struct Error2;
78//! # impl Error for Error2 {}
79//! # impl fmt::Display for Error2 {
80//! #    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81//! #        write!(f, "Error2")
82//! #    }
83//! # }
84//!
85//! #[derive(pretty_error_debug::Debug)]
86//! pub enum MyError {
87//!     Variant1(Error1),
88//!     Variant2(Error2),
89//! }
90//!
91//! impl fmt::Display for MyError {
92//!     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93//!         match self {
94//!             MyError::Variant1(_) => write!(f, "Error variant 1 happened"),
95//!             MyError::Variant2(_) => write!(f, "Error variant 2 happened"),
96//!         }
97//!     }
98//! }
99//!
100//! impl Error for MyError {
101//!     fn source(&self) -> Option<&(dyn 'static + Error)> {
102//!         match self {
103//!             MyError::Variant1(source) => Some(source),
104//!             MyError::Variant2(source) => Some(source),
105//!         }
106//!     }
107//! }
108//!
109//! fn main() -> Result<(), MyError> {
110//! # /*
111//!     ...
112//! # */ Ok(())
113//! }
114//! ```
115//!
116
117#![no_std]
118#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
119
120#[rustversion::before(1.81.0)]
121extern crate std;
122#[rustversion::since(1.81.0)]
123use core as std;
124
125mod implementation;
126#[cfg(test)]
127mod test;
128
129#[cfg(feature = "derive")]
130#[doc(hidden)]
131pub use core;
132use core::fmt;
133use std::error::Error;
134
135#[cfg(feature = "derive")]
136pub use pretty_error_debug_derive::PrettyDebug as Debug;
137
138pub use self::implementation::pretty_error_debug;
139
140/// Wrap an [`Error`] to display its error chain in debug messages ([`format!("{:?}")`][fmt::Debug]).
141///
142/// ## Example
143///
144/// ```rust
145/// use some_external_mod::{SomeError, some_test};
146///
147/// fn main() -> Result<(), pretty_error_debug::Wrapper<SomeError>> {
148///     some_test()?;
149///     Ok(())
150/// }
151///
152/// mod some_external_mod {
153///     #[derive(Debug)]
154///     pub struct SomeError;
155///
156///     impl std::error::Error for SomeError {}
157///
158///     impl std::fmt::Display for SomeError {
159///         fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160///             f.write_str("Something went wrong")
161///         }
162///     }
163///
164///     pub fn some_test() -> Result<(), SomeError> {
165/// # /*
166///         Err(SomeError)
167/// # */ Ok(())
168///     }
169/// }
170/// ```
171#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
172#[repr(transparent)]
173pub struct Wrapper<E: Error + ?Sized + 'static>(pub E);
174
175impl<E: Error + ?Sized + 'static> Wrapper<E> {
176    /// Return the wrapped argument.
177    #[inline]
178    pub const fn new(err: E) -> Self
179    where
180        E: Sized,
181    {
182        Self(err)
183    }
184}
185
186impl<E: Error + 'static> From<E> for Wrapper<E> {
187    #[inline]
188    fn from(value: E) -> Self {
189        Self(value)
190    }
191}
192
193impl<E: Error + 'static> Error for Wrapper<E> {
194    #[inline]
195    fn source(&self) -> Option<&(dyn Error + 'static)> {
196        Some(&self.0)
197    }
198}
199
200impl<E: Error + 'static> fmt::Display for Wrapper<E> {
201    #[inline]
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        fmt::Display::fmt(&self.0, f)
204    }
205}
206
207impl<E: Error + 'static> fmt::Debug for Wrapper<E> {
208    #[inline]
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210        pretty_error_debug(&self.0, f)
211    }
212}
213
214/// Wrap a reference to an [`Error`] to display its error chain with [`format!("{}")`][fmt::Display].
215#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
216#[repr(transparent)]
217pub struct Display<'a, E: Error + ?Sized>(pub &'a E);
218
219impl<'a, E: Error + ?Sized> Display<'a, E> {
220    /// Return the wrapped reference.
221    #[inline]
222    pub const fn new(err: &'a E) -> Self {
223        Self(err)
224    }
225}
226
227impl<'a, E: Error> From<&'a E> for Display<'a, E> {
228    #[inline]
229    fn from(value: &'a E) -> Self {
230        Self(value)
231    }
232}
233
234impl<E: Error + ?Sized> fmt::Debug for Display<'_, E> {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        f.debug_tuple("Display").field(&self.0).finish()
237    }
238}
239
240impl<E: Error + ?Sized> fmt::Display for Display<'_, E> {
241    #[inline]
242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        pretty_error_debug(&self.0, f)
244    }
245}