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}