err_report/
lib.rs

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