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