err_report/
lib.rs

1//! Clone of the unstable [`err_report::Report`] type.
2//!
3//! Backtrace support is omitted due to nightly requirement.
4//!
5//! Copied on 2025-09-09.
6//!
7//! # Examples
8//!
9//! ```
10//! use std::ffi::CString;
11//! use err_report::Report;
12//!
13//! let invalid_utf8 = vec![b'f', 0xff, b'o', b'o'];
14//! let cstring = CString::new(invalid_utf8).unwrap();
15//! let err = cstring.into_string().err().unwrap();
16//!
17//! // without Report
18//! assert_eq!(
19//!     "invalid utf-8 sequence of 1 bytes from index 1",
20//!     err.utf8_error().to_string(),
21//! );
22//! assert_eq!(
23//!     "C string contained non-utf8 bytes",
24//!     err.to_string(),
25//! );
26//!
27//! // with Report
28//! assert_eq!(
29//!     "invalid utf-8 sequence of 1 bytes from index 1",
30//!     Report::new(err.utf8_error()).to_string(),
31//! );
32//! assert_eq!(
33//!     "C string contained non-utf8 bytes: invalid utf-8 sequence of 1 bytes from index 1",
34//!     Report::new(err).to_string(),
35//! );
36//! ```
37
38pub use core::error::Error;
39// pub use core::error::{Request, request_ref, request_value};
40
41// use std::backtrace::Backtrace;
42use std::fmt::{self, Write as _};
43
44/// An error reporter that prints an error and its sources.
45///
46/// Report also exposes configuration options for formatting the error sources, either entirely on a
47/// single line, or in multi-line format with each source on a new line.
48///
49/// `Report` only requires that the wrapped error implement `Error`. It doesn't require that the
50/// wrapped error be `Send`, `Sync`, or `'static`.
51///
52/// # Examples
53///
54/// ```rust
55/// use std::{error::Error, fmt};
56///
57/// use err_report::Report;
58///
59/// #[derive(Debug)]
60/// struct SuperError {
61///     source: SuperErrorSideKick,
62/// }
63///
64/// impl fmt::Display for SuperError {
65///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66///         write!(f, "SuperError is here!")
67///     }
68/// }
69///
70/// impl Error for SuperError {
71///     fn source(&self) -> Option<&(dyn Error + 'static)> {
72///         Some(&self.source)
73///     }
74/// }
75///
76/// #[derive(Debug)]
77/// struct SuperErrorSideKick;
78///
79/// impl fmt::Display for SuperErrorSideKick {
80///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81///         write!(f, "SuperErrorSideKick is here!")
82///     }
83/// }
84///
85/// impl Error for SuperErrorSideKick {}
86///
87/// fn get_super_error() -> Result<(), SuperError> {
88///     Err(SuperError {
89///         source: SuperErrorSideKick,
90///     })
91/// }
92///
93/// fn main() {
94///     match get_super_error() {
95///         Err(e) => println!("Error: {}", Report::new(e)),
96///         _ => println!("No error"),
97///     }
98/// }
99/// ```
100///
101/// This example produces the following output:
102///
103/// ```console
104/// Error: SuperError is here!: SuperErrorSideKick is here!
105/// ```
106///
107/// ## Output consistency
108///
109/// Report prints the same output via `Display` and `Debug`, so it works well with
110/// [`Result::unwrap`]/[`Result::expect`] which print their `Err` variant via `Debug`:
111///
112/// ```should_panic
113/// use err_report::Report;
114/// # use std::error::Error;
115/// # use std::fmt;
116/// # #[derive(Debug)]
117/// # struct SuperError {
118/// #     source: SuperErrorSideKick,
119/// # }
120/// # impl fmt::Display for SuperError {
121/// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122/// #         write!(f, "SuperError is here!")
123/// #     }
124/// # }
125/// # impl Error for SuperError {
126/// #     fn source(&self) -> Option<&(dyn Error + 'static)> {
127/// #         Some(&self.source)
128/// #     }
129/// # }
130/// # #[derive(Debug)]
131/// # struct SuperErrorSideKick;
132/// # impl fmt::Display for SuperErrorSideKick {
133/// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134/// #         write!(f, "SuperErrorSideKick is here!")
135/// #     }
136/// # }
137/// # impl Error for SuperErrorSideKick {}
138/// # fn get_super_error() -> Result<(), SuperError> {
139/// #     Err(SuperError { source: SuperErrorSideKick })
140/// # }
141///
142/// get_super_error().map_err(Report::new).unwrap();
143/// ```
144///
145/// This example produces the following output:
146///
147/// ```console
148/// thread 'main' panicked at src/error.rs:34:40:
149/// called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!
150/// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
151/// ```
152///
153/// ## Return from `main`
154///
155/// `Report` also implements `From` for all types that implement [`Error`]; this when combined with
156/// the `Debug` output means `Report` is an ideal starting place for formatting errors returned
157/// from `main`.
158///
159/// ```should_panic
160/// use err_report::Report;
161/// # use std::error::Error;
162/// # use std::fmt;
163/// # #[derive(Debug)]
164/// # struct SuperError {
165/// #     source: SuperErrorSideKick,
166/// # }
167/// # impl fmt::Display for SuperError {
168/// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169/// #         write!(f, "SuperError is here!")
170/// #     }
171/// # }
172/// # impl Error for SuperError {
173/// #     fn source(&self) -> Option<&(dyn Error + 'static)> {
174/// #         Some(&self.source)
175/// #     }
176/// # }
177/// # #[derive(Debug)]
178/// # struct SuperErrorSideKick;
179/// # impl fmt::Display for SuperErrorSideKick {
180/// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181/// #         write!(f, "SuperErrorSideKick is here!")
182/// #     }
183/// # }
184/// # impl Error for SuperErrorSideKick {}
185/// # fn get_super_error() -> Result<(), SuperError> {
186/// #     Err(SuperError { source: SuperErrorSideKick })
187/// # }
188///
189/// fn main() -> Result<(), Report<SuperError>> {
190///     get_super_error()?;
191///     Ok(())
192/// }
193/// ```
194///
195/// This example produces the following output:
196///
197/// ```console
198/// Error: SuperError is here!: SuperErrorSideKick is here!
199/// ```
200///
201/// **Note**: `Report`s constructed via `?` and `From` will be configured to use the single line
202/// output format. If you want to make sure your `Report`s are pretty printed and include backtrace
203/// you will need to manually convert and enable those flags.
204///
205/// ```should_panic
206/// use err_report::Report;
207/// # use std::error::Error;
208/// # use std::fmt;
209/// # #[derive(Debug)]
210/// # struct SuperError {
211/// #     source: SuperErrorSideKick,
212/// # }
213/// # impl fmt::Display for SuperError {
214/// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215/// #         write!(f, "SuperError is here!")
216/// #     }
217/// # }
218/// # impl Error for SuperError {
219/// #     fn source(&self) -> Option<&(dyn Error + 'static)> {
220/// #         Some(&self.source)
221/// #     }
222/// # }
223/// # #[derive(Debug)]
224/// # struct SuperErrorSideKick;
225/// # impl fmt::Display for SuperErrorSideKick {
226/// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227/// #         write!(f, "SuperErrorSideKick is here!")
228/// #     }
229/// # }
230/// # impl Error for SuperErrorSideKick {}
231/// # fn get_super_error() -> Result<(), SuperError> {
232/// #     Err(SuperError { source: SuperErrorSideKick })
233/// # }
234///
235/// fn main() -> Result<(), Report<SuperError>> {
236///     get_super_error()
237///         .map_err(Report::from)
238///         .map_err(|r| r.pretty(true))?;
239///     Ok(())
240/// }
241/// ```
242///
243/// This example produces the following output:
244///
245/// ```console
246/// Error: SuperError is here!
247///
248/// Caused by:
249///       SuperErrorSideKick is here!
250/// ```
251pub struct Report<E = Box<dyn Error>> {
252    /// The error being reported.
253    error: E,
254    // /// Whether a backtrace should be included as part of the report.
255    // show_backtrace: bool,
256    /// Whether the report should be pretty-printed.
257    pretty: bool,
258}
259
260impl<E> Report<E>
261where
262    Report<E>: From<E>,
263{
264    /// Creates a new `Report` from an input error.
265    pub fn new(error: E) -> Report<E> {
266        Self::from(error)
267    }
268}
269
270impl<E> Report<E> {
271    /// Enable pretty-printing the report across multiple lines.
272    ///
273    /// # Examples
274    ///
275    /// ```rust
276    /// use err_report::Report;
277    /// # use std::error::Error;
278    /// # use std::fmt;
279    /// # #[derive(Debug)]
280    /// # struct SuperError {
281    /// #     source: SuperErrorSideKick,
282    /// # }
283    /// # impl fmt::Display for SuperError {
284    /// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285    /// #         write!(f, "SuperError is here!")
286    /// #     }
287    /// # }
288    /// # impl Error for SuperError {
289    /// #     fn source(&self) -> Option<&(dyn Error + 'static)> {
290    /// #         Some(&self.source)
291    /// #     }
292    /// # }
293    /// # #[derive(Debug)]
294    /// # struct SuperErrorSideKick;
295    /// # impl fmt::Display for SuperErrorSideKick {
296    /// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297    /// #         write!(f, "SuperErrorSideKick is here!")
298    /// #     }
299    /// # }
300    /// # impl Error for SuperErrorSideKick {}
301    ///
302    /// let error = SuperError {
303    ///     source: SuperErrorSideKick,
304    /// };
305    /// let report = Report::new(error).pretty(true);
306    /// eprintln!("Error: {report:?}");
307    /// ```
308    ///
309    /// This example produces the following output:
310    ///
311    /// ```console
312    /// Error: SuperError is here!
313    ///
314    /// Caused by:
315    ///       SuperErrorSideKick is here!
316    /// ```
317    ///
318    /// When there are multiple source errors the causes will be numbered in order of iteration
319    /// starting from the outermost error.
320    ///
321    /// ```rust
322    /// use err_report::Report;
323    /// # use std::error::Error;
324    /// # use std::fmt;
325    /// # #[derive(Debug)]
326    /// # struct SuperError {
327    /// #     source: SuperErrorSideKick,
328    /// # }
329    /// # impl fmt::Display for SuperError {
330    /// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331    /// #         write!(f, "SuperError is here!")
332    /// #     }
333    /// # }
334    /// # impl Error for SuperError {
335    /// #     fn source(&self) -> Option<&(dyn Error + 'static)> {
336    /// #         Some(&self.source)
337    /// #     }
338    /// # }
339    /// # #[derive(Debug)]
340    /// # struct SuperErrorSideKick {
341    /// #     source: SuperErrorSideKickSideKick,
342    /// # }
343    /// # impl fmt::Display for SuperErrorSideKick {
344    /// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345    /// #         write!(f, "SuperErrorSideKick is here!")
346    /// #     }
347    /// # }
348    /// # impl Error for SuperErrorSideKick {
349    /// #     fn source(&self) -> Option<&(dyn Error + 'static)> {
350    /// #         Some(&self.source)
351    /// #     }
352    /// # }
353    /// # #[derive(Debug)]
354    /// # struct SuperErrorSideKickSideKick;
355    /// # impl fmt::Display for SuperErrorSideKickSideKick {
356    /// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357    /// #         write!(f, "SuperErrorSideKickSideKick is here!")
358    /// #     }
359    /// # }
360    /// # impl Error for SuperErrorSideKickSideKick { }
361    ///
362    /// let source = SuperErrorSideKickSideKick;
363    /// let source = SuperErrorSideKick { source };
364    /// let error = SuperError { source };
365    /// let report = Report::new(error).pretty(true);
366    /// eprintln!("Error: {report:?}");
367    /// ```
368    ///
369    /// This example produces the following output:
370    ///
371    /// ```console
372    /// Error: SuperError is here!
373    ///
374    /// Caused by:
375    ///    0: SuperErrorSideKick is here!
376    ///    1: SuperErrorSideKickSideKick is here!
377    /// ```
378    pub fn pretty(mut self, pretty: bool) -> Self {
379        self.pretty = pretty;
380        self
381    }
382
383    // /// Display backtrace if available when using pretty output format.
384    // ///
385    // /// # Examples
386    // ///
387    // /// **Note**: Report will search for the first `Backtrace` it can find starting from the
388    // /// outermost error. In this example it will display the backtrace from the second error in the
389    // /// sources, `SuperErrorSideKick`.
390    // ///
391    // /// ```rust
392    // /// #![feature(error_generic_member_access)]
393    // /// # use std::error::Error;
394    // /// # use std::fmt;
395    // /// use std::error::Request;
396    // /// use err_report::Report;
397    // /// use std::backtrace::Backtrace;
398    // ///
399    // /// # #[derive(Debug)]
400    // /// # struct SuperError {
401    // /// #     source: SuperErrorSideKick,
402    // /// # }
403    // /// # impl fmt::Display for SuperError {
404    // /// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405    // /// #         write!(f, "SuperError is here!")
406    // /// #     }
407    // /// # }
408    // /// # impl Error for SuperError {
409    // /// #     fn source(&self) -> Option<&(dyn Error + 'static)> {
410    // /// #         Some(&self.source)
411    // /// #     }
412    // /// # }
413    // /// #[derive(Debug)]
414    // /// struct SuperErrorSideKick {
415    // ///     backtrace: Backtrace,
416    // /// }
417    // ///
418    // /// impl SuperErrorSideKick {
419    // ///     fn new() -> SuperErrorSideKick {
420    // ///         SuperErrorSideKick { backtrace: Backtrace::force_capture() }
421    // ///     }
422    // /// }
423    // ///
424    // /// impl Error for SuperErrorSideKick {
425    // ///     fn provide<'a>(&'a self, request: &mut Request<'a>) {
426    // ///         request.provide_ref::<Backtrace>(&self.backtrace);
427    // ///     }
428    // /// }
429    // ///
430    // /// // The rest of the example is unchanged ...
431    // /// # impl fmt::Display for SuperErrorSideKick {
432    // /// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
433    // /// #         write!(f, "SuperErrorSideKick is here!")
434    // /// #     }
435    // /// # }
436    // ///
437    // /// let source = SuperErrorSideKick::new();
438    // /// let error = SuperError { source };
439    // /// let report = Report::new(error).pretty(true).show_backtrace(true);
440    // /// eprintln!("Error: {report:?}");
441    // /// ```
442    // ///
443    // /// This example produces something similar to the following output:
444    // ///
445    // /// ```console
446    // /// Error: SuperError is here!
447    // ///
448    // /// Caused by:
449    // ///       SuperErrorSideKick is here!
450    // ///
451    // /// Stack backtrace:
452    // ///    0: rust_out::main::_doctest_main_src_error_rs_1158_0::SuperErrorSideKick::new
453    // ///    1: rust_out::main::_doctest_main_src_error_rs_1158_0
454    // ///    2: rust_out::main
455    // ///    3: core::ops::function::FnOnce::call_once
456    // ///    4: std::sys::backtrace::__rust_begin_short_backtrace
457    // ///    5: std::rt::lang_start::{{closure}}
458    // ///    6: std::panicking::try
459    // ///    7: std::rt::lang_start_internal
460    // ///    8: std::rt::lang_start
461    // ///    9: main
462    // ///   10: __libc_start_main
463    // ///   11: _start
464    // /// ```
465    // pub fn show_backtrace(mut self, show_backtrace: bool) -> Self {
466    //     self.show_backtrace = show_backtrace;
467    //     self
468    // }
469}
470
471impl<E> Report<E>
472where
473    E: Error,
474{
475    // fn backtrace(&self) -> Option<&Backtrace> {
476    //     // have to grab the backtrace on the first error directly since that error may not be
477    //     // 'static
478    //     let backtrace = request_ref(&self.error);
479    //     let backtrace = backtrace.or_else(|| {
480    //         self.error
481    //             .source()
482    //             .map(|source| Source::new(source).find_map(|source| request_ref(source)))
483    //             .flatten()
484    //     });
485    //     backtrace
486    // }
487
488    /// Format the report as a single line.
489    fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490        write!(f, "{}", self.error)?;
491
492        let sources = self.error.source().into_iter().flat_map(Source::new);
493
494        for cause in sources {
495            write!(f, ": {cause}")?;
496        }
497
498        Ok(())
499    }
500
501    /// Format the report as multiple lines, with each error cause on its own line.
502    fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
503        let error = &self.error;
504
505        write!(f, "{error}")?;
506
507        if let Some(cause) = error.source() {
508            write!(f, "\n\nCaused by:")?;
509
510            let multiple = cause.source().is_some();
511
512            for (ind, error) in Source::new(cause).enumerate() {
513                writeln!(f)?;
514                let mut indented = Indented { inner: f };
515                if multiple {
516                    write!(indented, "{ind: >4}: {error}")?;
517                } else {
518                    write!(indented, "      {error}")?;
519                }
520            }
521        }
522
523        // if self.show_backtrace {
524        //     if let Some(backtrace) = self.backtrace() {
525        //         write!(
526        //             f,
527        //             "\n\nStack backtrace:\n{}",
528        //             backtrace.to_string().trim_end()
529        //         )?;
530        //     }
531        // }
532
533        Ok(())
534    }
535}
536
537impl<E> From<E> for Report<E>
538where
539    E: Error,
540{
541    fn from(error: E) -> Self {
542        Report {
543            error,
544            // show_backtrace: false,
545            pretty: false,
546        }
547    }
548}
549
550impl<E> fmt::Display for Report<E>
551where
552    E: Error,
553{
554    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
555        if self.pretty {
556            self.fmt_multiline(f)
557        } else {
558            self.fmt_singleline(f)
559        }
560    }
561}
562
563// This type intentionally outputs the same format for `Display` and `Debug`for
564// situations where you unwrap a `Report` or return it from main.
565impl<E> fmt::Debug for Report<E>
566where
567    Report<E>: fmt::Display,
568{
569    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
570        fmt::Display::fmt(self, f)
571    }
572}
573
574/// Wrapper type for indenting the inner source.
575struct Indented<'a, D> {
576    inner: &'a mut D,
577}
578
579impl<T> fmt::Write for Indented<'_, T>
580where
581    T: fmt::Write,
582{
583    fn write_str(&mut self, s: &str) -> fmt::Result {
584        for (i, line) in s.split('\n').enumerate() {
585            if i > 0 {
586                self.inner.write_char('\n')?;
587                self.inner.write_str("      ")?;
588            }
589
590            self.inner.write_str(line)?;
591        }
592
593        Ok(())
594    }
595}
596
597/// An iterator over an [`Error`] and its sources.
598///
599/// If you want to omit the initial error and only process
600/// its sources, use `skip(1)`.
601#[derive(Clone, Debug)]
602struct Source<'a> {
603    current: Option<&'a (dyn Error + 'static)>,
604}
605
606impl<'a> Source<'a> {
607    fn new(error: &'a (dyn Error + 'static)) -> Self {
608        Self {
609            current: Some(error),
610        }
611    }
612}
613
614impl<'a> Iterator for Source<'a> {
615    type Item = &'a (dyn Error + 'static);
616
617    fn next(&mut self) -> Option<Self::Item> {
618        let current = self.current;
619        self.current = self.current.and_then(Error::source);
620        current
621    }
622
623    fn size_hint(&self) -> (usize, Option<usize>) {
624        if self.current.is_some() {
625            (1, None)
626        } else {
627            (0, Some(0))
628        }
629    }
630}