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}