error_trace/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))]
2// LIB.rs
3// by Lut99
4//
5// Created:
6// 22 Sep 2023, 12:17:19
7// Last edited:
8// 05 Nov 2024, 11:38:27
9// Auto updated?
10// Yes
11//
12// Description:
13//! Small Rust crate for printing nice errors traits based on [`Error::source()`].
14//!
15//! # Usage
16//! Using the crate is quite straightforward.
17//!
18//! First, create your errors as usual:
19//! ```rust
20//! # use std::error::Error;
21//! # use std::fmt::{Display, Formatter, Result as FResult};
22//! #[derive(Debug)]
23//! struct SomeError {
24//! msg : String,
25//! }
26//! impl Display for SomeError {
27//! fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
28//! write!(f, "{}", self.msg)
29//! }
30//! }
31//! impl Error for SomeError {}
32//!
33//! #[derive(Debug)]
34//! struct HigherError {
35//! msg : String,
36//! child : SomeError,
37//! }
38//! impl Display for HigherError {
39//! fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
40//! write!(f, "{}", self.msg)
41//! }
42//! }
43//! impl Error for HigherError {
44//! fn source(&self) -> Option<&(dyn 'static + Error)> {
45//! Some(&self.child)
46//! }
47//! }
48//! ```
49//!
50//! Then, when it is time to report them to the user, do not print them directly but instead use the `ErrorTrace`-trait's `trace()` function:
51//! ```rust
52//! use error_trace::ErrorTrace as _;
53//!
54//! # use std::error::Error;
55//! # use std::fmt::{Display, Formatter, Result as FResult};
56//! # #[derive(Debug)]
57//! # struct SomeError {
58//! # msg : String,
59//! # }
60//! # impl Display for SomeError {
61//! # fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
62//! # write!(f, "{}", self.msg)
63//! # }
64//! # }
65//! # impl Error for SomeError {}
66//! #
67//! # #[derive(Debug)]
68//! # struct HigherError {
69//! # msg : String,
70//! # child : SomeError,
71//! # }
72//! # impl Display for HigherError {
73//! # fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
74//! # write!(f, "{}", self.msg)
75//! # }
76//! # }
77//! # impl Error for HigherError {
78//! # fn source(&self) -> Option<&(dyn 'static + Error)> {
79//! # Some(&self.child)
80//! # }
81//! # }
82//! // ...
83//!
84//! let err: HigherError = HigherError {
85//! msg: "Oh no, something went wrong!".into(),
86//! child: SomeError{
87//! msg: "A specific reason".into()
88//! }
89//! };
90//! eprintln!("{}", err.trace());
91//! ```
92//! This will show you:
93//! ```text
94//! Oh no, something went wrong!
95//!
96//! Caused by:
97//! o A specific reason
98//! ```
99//!
100//! If you enable the `colors`-feature, you can additionally print some neat colors:
101//! ```rust
102//! use error_trace::ErrorTrace as _;
103//!
104//! # use std::error::Error;
105//! # use std::fmt::{Display, Formatter, Result as FResult};
106//! # #[derive(Debug)]
107//! # struct SomeError {
108//! # msg : String,
109//! # }
110//! # impl Display for SomeError {
111//! # fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
112//! # write!(f, "{}", self.msg)
113//! # }
114//! # }
115//! # impl Error for SomeError {}
116//! #
117//! # #[derive(Debug)]
118//! # struct HigherError {
119//! # msg : String,
120//! # child : SomeError,
121//! # }
122//! # impl Display for HigherError {
123//! # fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
124//! # write!(f, "{}", self.msg)
125//! # }
126//! # }
127//! # impl Error for HigherError {
128//! # fn source(&self) -> Option<&(dyn 'static + Error)> {
129//! # Some(&self.child)
130//! # }
131//! # }
132//! // ...
133//!
134//! let err: HigherError = HigherError {
135//! msg: "Oh no, something went wrong!".into(),
136//! child: SomeError{
137//! msg: "A specific reason".into()
138//! }
139//! };
140//!
141//! // Requires the `colors`-feature!
142//! eprintln!("{}", err.trace_colored());
143//! ```
144//! 
145//!
146//! Finally, when used in a situation where you want to show a quick error but are sure to never
147//! needs its contents, you can use the [`toplevel!()`]-macro:
148//! ```rust
149//! use error_trace::toplevel;
150//!
151//! // Do something that fails
152//! let err = std::str::from_utf8(&[0xFF]).unwrap_err();
153//!
154//! // Format it with a one-time parent error
155//! eprintln!("{}", toplevel!(("Oh no, everything went wrong!"), err));
156//! ```
157//!
158//! For users of the `colors`-feature, there is the associated [`toplevel_colored!()`]-macro:
159//! ```rust
160//! use error_trace::toplevel_colored;
161//!
162//! // Do something that fails
163//! let err = std::str::from_utf8(&[0xFF]).unwrap_err();
164//!
165//! // Format it with a one-time parent error
166//! eprintln!("{}", toplevel_colored!(("Oh no, everything went wrong!"), err));
167//! ```
168//!
169//!
170//! # Installation
171//! To use this crate into one of your projects, simply add it to your `Cargo.toml` file:
172//! ```toml
173//! error-trace = { git = "https://github.com/Lut99/error-trace-rs" }
174//! ```
175//! Optionally, you can commit to a particular tag:
176//! ```toml
177//! error-trace = { git = "https://github.com/Lut99/error-trace-rs", tag = "v4.0.0" }
178//! ```
179//!
180//! To build this crate's documentation and open it, run:
181//! ```bash
182//! cargo doc --all-features --no-deps --open
183//! ```
184//! in the root of the repository.
185//!
186//! ## Features
187//! The crate has the following features:
188//! - `colors`: Enables the use of [`ErrorTrace::trace_colored()`].
189//! - `macros`: Enables the use of the [`toplevel!()`]- and [`toplevel_colored!()`]-macros.
190//! - `serde`: Implements `Deserialize` and `Serialize` for the [`FrozenTrace`]-structure.
191//
192
193// Modules
194#[cfg(test)]
195mod tests;
196
197// Imports
198use std::borrow::Cow;
199use std::error::Error;
200use std::fmt::{Display, Formatter, Result as FResult};
201
202#[cfg(feature = "colors")]
203use console::style;
204
205
206/***** MACROS *****/
207/// Creates a one-time [`ErrorTrace`]-compatible type from the given string, then calls [`trace()`](ErrorTrace::trace()) on it.
208///
209/// # Arguments
210/// The macro has the following signature:
211/// ```plain
212/// (`$($args:tt)*), $err:expr
213/// ```
214/// - `$($args:tt)*`: A message to use for the toplevel error. This can be given the arguments to a [`format!`]-call.
215/// - `$err:expr`: The error to embed in the newly built type.
216///
217/// # Returns
218/// An [`ErrorTraceFormatter`] that can be displayed immediately.
219///
220/// # Example
221/// ```rust
222/// use error_trace::toplevel;
223///
224/// // Do something that fails
225/// let err = std::str::from_utf8(&[0xFF]).unwrap_err();
226///
227/// // Format it with a one-time parent error
228/// assert_eq!(
229/// toplevel!(("Oh no, everything went wrong!"), err).to_string(),
230/// r#"Oh no, everything went wrong!
231///
232/// Caused by:
233/// o invalid utf-8 sequence of 1 bytes from index 0
234///
235/// "#
236/// );
237/// ```
238/// One can use full format strings for the message:
239/// ```rust
240/// use error_trace::toplevel;
241///
242/// // Do something that fails
243/// let bytes: [u8; 1] = [0xFF];
244/// let err = std::str::from_utf8(&bytes).unwrap_err();
245///
246/// // Format it with a one-time parent error
247/// assert_eq!(
248/// toplevel!(("Failed to parse '{:?}'", bytes.as_slice()), err).to_string(),
249/// r#"Failed to parse '[255]'
250///
251/// Caused by:
252/// o invalid utf-8 sequence of 1 bytes from index 0
253///
254/// "#
255/// );
256///
257///
258/// // Equivalent to above (but using a neater format syntax!)
259/// assert_eq!(
260/// toplevel!(("Failed to parse '{bytes:?}'"), err).to_string(),
261/// r#"Failed to parse '[255]'
262///
263/// Caused by:
264/// o invalid utf-8 sequence of 1 bytes from index 0
265///
266/// "#
267/// );
268/// ```
269#[cfg(feature = "macros")]
270#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
271#[macro_export]
272macro_rules! toplevel {
273 (($($args:tt)*), $err:expr) => {
274 $crate::ErrorTraceFormatter::new(format!($($args)*), Some(&$err))
275 };
276}
277
278/// Creates a one-time [`ErrorTrace`]-compatible type from the given string, then calls [`trace_colored()`](ErrorTrace::trace_colored()) on it.
279///
280/// # Arguments
281/// The macro has the following signature:
282/// ```plain
283/// ($($args:tt)*), $err:expr
284/// ```
285/// - `$($args:tt)*`: A message to use for the toplevel error. This can be given the arguments to a [`format!`]-call.
286/// - `$err:expr`: The error to embed in the newly built type.
287///
288/// # Returns
289/// An [`ErrorTraceColorFormatter`] that can be displayed immediately.
290///
291/// # Example
292/// ```rust
293/// use error_trace::toplevel_colored;
294///
295/// // Do something that fails
296/// let err = std::str::from_utf8(&[0xFF]).unwrap_err();
297///
298/// // Colours aren't visible here, because we're writing to a string; but try writing to stdout/stderr!
299/// assert_eq!(
300/// toplevel_colored!(("Oh no, everything went wrong!"), err).to_string(),
301/// r#"Oh no, everything went wrong!
302///
303/// Caused by:
304/// o invalid utf-8 sequence of 1 bytes from index 0
305///
306/// "#
307/// );
308/// ```
309/// One can use full format strings for the message:
310/// ```rust
311/// use error_trace::toplevel_colored;
312///
313/// // Do something that fails
314/// let bytes: [u8; 1] = [0xFF];
315/// let err = std::str::from_utf8(&bytes).unwrap_err();
316///
317/// // Format it with a one-time parent error
318/// assert_eq!(
319/// toplevel_colored!(("Failed to parse '{:?}'", bytes.as_slice()), err).to_string(),
320/// r#"Failed to parse '[255]'
321///
322/// Caused by:
323/// o invalid utf-8 sequence of 1 bytes from index 0
324///
325/// "#
326/// );
327///
328///
329/// // Equivalent to above (but using a neater format syntax!)
330/// assert_eq!(
331/// toplevel_colored!(("Failed to parse '{bytes:?}'"), err).to_string(),
332/// r#"Failed to parse '[255]'
333///
334/// Caused by:
335/// o invalid utf-8 sequence of 1 bytes from index 0
336///
337/// "#
338/// );
339/// ```
340#[cfg(all(feature = "colors", feature = "macros"))]
341#[cfg_attr(docsrs, doc(cfg(all(feature = "colors", feature = "macros"))))]
342#[macro_export]
343macro_rules! toplevel_colored {
344 (($($args:tt)*), $err:expr) => {
345 $crate::ErrorTraceColorFormatter::new(format!($($args)*), Some(&$err))
346 };
347}
348
349
350
351
352
353/***** FORMATTERS *****/
354/// Formats an error and all its dependencies.
355///
356/// If you have the `colors`-feature enabled, then you can also use [`ErrorTraceColorFormatter`] to
357/// do the same but with ANSI-colors.
358///
359/// # Example
360/// ```rust
361/// # use std::error::Error;
362/// # use std::fmt::{Display, Formatter, Result as FResult};
363/// use error_trace::{ErrorTrace as _, ErrorTraceFormatter};
364///
365/// #[derive(Debug)]
366/// struct ExampleError {
367/// msg: String,
368/// }
369/// impl Display for ExampleError {
370/// fn fmt(&self, f: &mut Formatter<'_>) -> FResult { write!(f, "{}", self.msg) }
371/// }
372/// impl Error for ExampleError {}
373///
374/// let err = ExampleError { msg: "Hello, world!".into() };
375/// let fmt: ErrorTraceFormatter = err.trace();
376/// assert_eq!(format!("{fmt}"), "Hello, world!");
377/// ```
378pub struct ErrorTraceFormatter<'s, 'e1, 'e2> {
379 /// The message that is the main error message.
380 msg: Cow<'s, str>,
381 /// An optional nested error to format that is the first element in the tree.
382 err: Option<&'e1 (dyn 'e2 + Error)>,
383}
384impl<'s, 'e1, 'e2> ErrorTraceFormatter<'s, 'e1, 'e2> {
385 /// Builds a formatter for a given "anonymous error".
386 ///
387 /// This is useful for creating one-time error traces where you don't want to create the root type.
388 ///
389 /// For even more convenience, see the [`toplevel!`]-macro.
390 ///
391 /// # Arguments
392 /// - `msg`: A message that is printed as "current error".
393 /// - `err`: An optional error that, if any, will cause this formatter to start printing a
394 /// trace based on the error's [`Error::source()`]-implementation.
395 ///
396 /// # Returns
397 /// A new ErrorTraceFormatter ready to rock-n-roll.
398 #[inline]
399 pub fn new(msg: impl Into<Cow<'s, str>>, err: Option<&'e1 (dyn 'e2 + Error)>) -> Self { Self { msg: msg.into(), err } }
400}
401impl<'s, 'e1, 'e2> Display for ErrorTraceFormatter<'s, 'e1, 'e2> {
402 fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
403 // Match on beautiness
404 if f.alternate() {
405 // Always print the thing
406 write!(f, "{:#}", self.msg)?;
407
408 // Print any deps if any
409 if let Some(source) = self.err {
410 // Write the thingy
411 write!(f, "\n\nCaused by:")?;
412
413 let mut source: Option<&dyn Error> = Some(source);
414 while let Some(err) = source.take() {
415 // Print it
416 write!(f, "\n o {err:#}")?;
417 source = err.source();
418 }
419
420 // Write closing enters
421 writeln!(f, "\n")?;
422 }
423 } else {
424 // Always print the thing
425 write!(f, "{}", self.msg)?;
426
427 // Print any deps if any
428 if let Some(source) = self.err {
429 // Write the thingy
430 write!(f, "\n\nCaused by:")?;
431
432 let mut source: Option<&dyn Error> = Some(source);
433 while let Some(err) = source.take() {
434 // Print it
435 write!(f, "\n o {err}")?;
436 source = err.source();
437 }
438
439 // Write closing enters
440 writeln!(f, "\n")?;
441 }
442 }
443
444 // Done!
445 Ok(())
446 }
447}
448
449/// Formats an error and all its dependencies using neat ANSI-colors if the formatter to which
450/// we're writing supports it.
451///
452/// Whether colors are enabled or not can be checked by [`console`]'s
453/// [`colors_enabled_stderr()`](console::colors_enabled_stderr()) function, and controlled by
454/// [`set_colors_enabled_stderr()`](console::set_colors_enabled_stderr()).
455///
456/// See [`ErrorTraceFormatter`] to do the same but without ANSI colors at all.
457///
458/// # Example
459/// ```rust
460/// # use std::error::Error;
461/// # use std::fmt::{Display, Formatter, Result as FResult};
462/// use error_trace::{ErrorTraceColorFormatter, ErrorTrace as _};
463///
464/// #[derive(Debug)]
465/// struct ExampleError {
466/// msg : String,
467/// }
468/// impl Display for ExampleError {
469/// fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
470/// write!(f, "{}", self.msg)
471/// }
472/// }
473/// impl Error for ExampleError {}
474///
475/// let err = ExampleError { msg: "Hello, world!".into() };
476/// let fmt: ErrorTraceColorFormatter = err.trace_colored();
477///
478/// // Colours aren't visible here, because we're writing to a string; but try writing to stdout/stderr!
479/// assert_eq!(format!("{fmt}"), "Hello, world!");
480/// ```
481#[cfg(feature = "colors")]
482#[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
483pub struct ErrorTraceColorFormatter<'s, 'e1, 'e2> {
484 /// The message that is the main error message.
485 msg: Cow<'s, str>,
486 /// An optional nested error to format that is the first element in the tree.
487 err: Option<&'e1 (dyn 'e2 + Error)>,
488}
489#[cfg(feature = "colors")]
490#[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
491impl<'s, 'e1, 'e2> ErrorTraceColorFormatter<'s, 'e1, 'e2> {
492 /// Builds a formatter for a given "anonymous error".
493 ///
494 /// This is useful for creating one-time error traces where you don't want to create the root type.
495 ///
496 /// For even more convenience, see the [`toplevel!`]-macro.
497 ///
498 /// # Arguments
499 /// - `msg`: A message that is printed as "current error".
500 /// - `err`: An optional error that, if any, will cause this formatter to start printing a trace based on the error's [`Error::source()`]-implementation.
501 ///
502 /// # Returns
503 /// A new ErrorTraceColourFormatter ready to rock-n-roll.
504 #[inline]
505 pub fn new(msg: impl Into<Cow<'s, str>>, err: Option<&'e1 (dyn 'e2 + Error)>) -> Self { Self { msg: msg.into(), err } }
506}
507#[cfg(feature = "colors")]
508#[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
509impl<'s, 'e1, 'e2> Display for ErrorTraceColorFormatter<'s, 'e1, 'e2> {
510 fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
511 // Match on beautiness
512 if f.alternate() {
513 // Always print the thing
514 write!(f, "{}", style(format!("{:#}", self.msg)).for_stderr().bold())?;
515
516 // Print any deps if any
517 if let Some(source) = self.err {
518 // Write the thingy
519 write!(f, "\n\n{}", style("Caused by:").for_stderr().red().bold())?;
520
521 let mut source: Option<&dyn Error> = Some(source);
522 while let Some(err) = source.take() {
523 // Print it
524 write!(f, "\n o {}", style(format!("{err:#}")).for_stderr().bold())?;
525 source = err.source();
526 }
527
528 // Write closing enters
529 writeln!(f, "\n")?;
530 }
531 } else {
532 // Always print the thing
533 write!(f, "{}", style(&self.msg).for_stderr().bold())?;
534
535 // Print any deps if any
536 if let Some(source) = self.err {
537 // Write the thingy
538 write!(f, "\n\n{}", style("Caused by:").for_stderr().red().bold())?;
539
540 let mut source: Option<&dyn Error> = Some(source);
541 while let Some(err) = source.take() {
542 // Print it
543 write!(f, "\n o {}", style(err).for_stderr().bold())?;
544 source = err.source();
545 }
546
547 // Write closing enters
548 writeln!(f, "\n")?;
549 }
550 }
551
552 // Done!
553 Ok(())
554 }
555}
556
557
558
559
560
561/***** AUXILLARY *****/
562/// A helper type that can be used to "freeze" a trace and then pass it on to a further error.
563///
564/// This is useful in case you're dealing with errors where you don't want to propagate the type
565/// (e.g., due to lifetimes) but do want to propagate the trace.
566///
567/// # Example
568/// ```rust
569/// use std::error::Error;
570/// use std::fmt::{Display, Formatter, Result as FResult};
571///
572/// use error_trace::{ErrorTrace as _, FrozenTrace};
573///
574/// #[derive(Debug)]
575/// struct SomeError {
576/// msg: String,
577/// }
578/// impl Display for SomeError {
579/// fn fmt(&self, f: &mut Formatter<'_>) -> FResult { write!(f, "{}", self.msg) }
580/// }
581/// impl Error for SomeError {}
582///
583/// #[derive(Debug)]
584/// struct HigherError {
585/// msg: String,
586/// child: SomeError,
587/// }
588/// impl Display for HigherError {
589/// fn fmt(&self, f: &mut Formatter<'_>) -> FResult { write!(f, "{}", self.msg) }
590/// }
591/// impl Error for HigherError {
592/// fn source(&self) -> Option<&(dyn 'static + Error)> { Some(&self.child) }
593/// }
594///
595///
596///
597/// let err = HigherError {
598/// msg: "Oh no, something went wrong!".into(),
599/// child: SomeError { msg: "A specific reason".into() },
600/// };
601/// assert_eq!(
602/// FrozenTrace::new(err).trace().to_string(),
603/// r#"Oh no, something went wrong!
604///
605/// Caused by:
606/// o A specific reason
607///
608/// "#
609/// );
610/// ```
611#[derive(Clone, Debug)]
612#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
613pub struct FrozenTrace {
614 /// The error on this level.
615 pub message: String,
616 /// The error on the next level, if any.
617 pub source: Option<Box<Self>>,
618}
619impl FrozenTrace {
620 /// Builds a new FrozenTrace from the given [`Error`].
621 ///
622 /// # Arguments
623 /// - `err`: The error to walk and to freeze the errors of by serializing the messages.
624 ///
625 /// # Returns
626 /// A new FrozenTrace that itself implements [`Error`] again.
627 ///
628 /// # Example
629 /// See [`FrozenTrace`] itself for an example of how to use it, or see [`ErrorTrace::freeze()`].
630 #[inline]
631 pub fn new(err: impl Error) -> Self { Self { message: err.to_string(), source: err.source().map(|err| Box::new(Self::new(err))) } }
632
633 /// Builds a new Trace from a single [`String`].
634 ///
635 /// # Arguments
636 /// - `msg`: The (already serialized) message to wrap this trace around.
637 ///
638 /// # Returns
639 /// A new Trace that wraps the `msg` implementing [`Error`].
640 ///
641 /// # Example
642 /// ```rust
643 /// use std::error::Error as _;
644 ///
645 /// use error_trace::{ErrorTrace as _, FrozenTrace};
646 ///
647 /// let trace = FrozenTrace::from_msg("Hello there!");
648 /// assert_eq!(trace.trace().to_string(), "Hello there!");
649 /// ```
650 #[inline]
651 pub fn from_msg(msg: impl Into<String>) -> Self { Self { message: msg.into(), source: None } }
652
653 /// Builds a new Trace from a message and a source [`Error`].
654 ///
655 /// # Arguments
656 /// - `msg`: Some toplevel to show as root cause.
657 /// - `err`: The first error of the trace that causes `msg`.
658 ///
659 /// # Returns
660 /// A new Trace that wraps the `msg` as error, with `err` as trace, and that implements [`Error`].
661 #[inline]
662 pub fn from_source(msg: impl Into<String>, err: impl Error) -> Self { Self { message: msg.into(), source: Some(Box::new(err.freeze())) } }
663
664 /// Returns this Trace as an [`Error`] trait object.
665 ///
666 /// # Returns
667 /// A [`&'static dyn Error`](Error) which is even static!
668 #[inline]
669 pub fn as_error(&self) -> &(dyn 'static + Error) { self }
670}
671impl Display for FrozenTrace {
672 #[inline]
673 fn fmt(&self, f: &mut Formatter<'_>) -> FResult { write!(f, "{}", self.message) }
674}
675impl Error for FrozenTrace {
676 #[inline]
677 fn source(&self) -> Option<&(dyn Error + 'static)> { self.source.as_ref().map(|src| src.as_error()) }
678}
679
680
681
682
683
684/***** LIBRARY *****/
685/// Allows one to write an error and all of its dependencies.
686///
687/// # Example
688/// ```rust
689/// use std::error::Error;
690/// use std::fmt::{Display, Formatter, Result as FResult};
691///
692/// use error_trace::ErrorTrace as _;
693///
694/// #[derive(Debug)]
695/// struct SomeError {
696/// msg: String,
697/// }
698/// impl Display for SomeError {
699/// fn fmt(&self, f: &mut Formatter<'_>) -> FResult { write!(f, "{}", self.msg) }
700/// }
701/// impl Error for SomeError {}
702///
703/// #[derive(Debug)]
704/// struct HigherError {
705/// msg: String,
706/// child: SomeError,
707/// }
708/// impl Display for HigherError {
709/// fn fmt(&self, f: &mut Formatter<'_>) -> FResult { write!(f, "{}", self.msg) }
710/// }
711/// impl Error for HigherError {
712/// fn source(&self) -> Option<&(dyn 'static + Error)> { Some(&self.child) }
713/// }
714///
715///
716///
717/// let err = HigherError {
718/// msg: "Oh no, something went wrong!".into(),
719/// child: SomeError { msg: "A specific reason".into() },
720/// };
721/// assert_eq!(
722/// err.trace().to_string(),
723/// r#"Oh no, something went wrong!
724///
725/// Caused by:
726/// o A specific reason
727///
728/// "#
729/// );
730/// ```
731pub trait ErrorTrace: Error {
732 /// "Freezes" the trace of this error.
733 ///
734 /// This is useful in case you're dealing with errors where you don't want to propagate the
735 /// type (e.g., due to lifetimes) but do want to propagate the trace.
736 ///
737 /// # Returns
738 /// A [`FrozenTrace`] that returns the same trace when formatter, except all errors are
739 /// serialized to [`String`]s.
740 ///
741 /// # Example
742 /// ```rust
743 /// # use std::error::Error;
744 /// # use std::fmt::{Display, Formatter, Result as FResult};
745 /// #
746 /// use error_trace::ErrorTrace as _;
747 ///
748 /// # #[derive(Debug)]
749 /// # struct SomeError {
750 /// # msg : String,
751 /// # }
752 /// # impl Display for SomeError {
753 /// # fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
754 /// # write!(f, "{}", self.msg)
755 /// # }
756 /// # }
757 /// # impl Error for SomeError {}
758 /// #
759 /// # #[derive(Debug)]
760 /// # struct HigherError {
761 /// # msg : String,
762 /// # child : SomeError,
763 /// # }
764 /// # impl Display for HigherError {
765 /// # fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
766 /// # write!(f, "{}", self.msg)
767 /// # }
768 /// # }
769 /// # impl Error for HigherError {
770 /// # fn source(&self) -> Option<&(dyn 'static + Error)> {
771 /// # Some(&self.child)
772 /// # }
773 /// # }
774 /// #
775 /// #
776 /// #
777 /// let err = HigherError {
778 /// msg: "Oh no, something went wrong!".into(),
779 /// child: SomeError { msg: "A specific reason".into() },
780 /// };
781 /// assert_eq!(
782 /// err.freeze().trace().to_string(),
783 /// r#"Oh no, something went wrong!
784 ///
785 /// Caused by:
786 /// o A specific reason
787 ///
788 /// "#
789 /// );
790 /// ```
791 fn freeze(&self) -> FrozenTrace;
792
793
794
795 /// Returns a formatter for showing this Error and all its [source](Error::source())s.
796 ///
797 /// This function can be used similarly to [`Path::display()`](std::path::Path::display()),
798 /// since its result implements [`Display`].
799 ///
800 /// # Returns
801 /// A new [`ErrorTraceFormatter`] that implements [`Display`].
802 ///
803 /// # Example
804 /// ```rust
805 /// # use std::error::Error;
806 /// # use std::fmt::{Display, Formatter, Result as FResult};
807 /// #
808 /// use error_trace::ErrorTrace as _;
809 ///
810 /// # #[derive(Debug)]
811 /// # struct SomeError {
812 /// # msg : String,
813 /// # }
814 /// # impl Display for SomeError {
815 /// # fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
816 /// # write!(f, "{}", self.msg)
817 /// # }
818 /// # }
819 /// # impl Error for SomeError {}
820 /// #
821 /// # #[derive(Debug)]
822 /// # struct HigherError {
823 /// # msg : String,
824 /// # child : SomeError,
825 /// # }
826 /// # impl Display for HigherError {
827 /// # fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
828 /// # write!(f, "{}", self.msg)
829 /// # }
830 /// # }
831 /// # impl Error for HigherError {
832 /// # fn source(&self) -> Option<&(dyn 'static + Error)> {
833 /// # Some(&self.child)
834 /// # }
835 /// # }
836 /// #
837 /// #
838 /// #
839 /// let err = HigherError { msg: "Oh no, something went wrong!".into(), child: SomeError { msg: "A specific reason".into() } };
840 /// assert_eq!(err.trace().to_string(), r#"Oh no, something went wrong!
841 ///
842 /// Caused by:
843 /// o A specific reason
844 ///
845 /// "#);
846 fn trace(&self) -> ErrorTraceFormatter;
847
848 /// Returns a formatter for showing this Error and all its [source](Error::source())s with nice colors.
849 ///
850 /// This function can be used similarly to [`Path::display()`](std::path::Path::display()),
851 /// since its result implements [`Display`].
852 ///
853 /// # Returns
854 /// A new [`ErrorTraceColorFormatter`] that implements [`Display`].
855 ///
856 /// # Example
857 /// ```rust
858 /// # use std::error::Error;
859 /// # use std::fmt::{Display, Formatter, Result as FResult};
860 /// #
861 /// use error_trace::ErrorTrace as _;
862 ///
863 /// # #[derive(Debug)]
864 /// # struct SomeError {
865 /// # msg : String,
866 /// # }
867 /// # impl Display for SomeError {
868 /// # fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
869 /// # write!(f, "{}", self.msg)
870 /// # }
871 /// # }
872 /// # impl Error for SomeError {}
873 /// #
874 /// # #[derive(Debug)]
875 /// # struct HigherError {
876 /// # msg : String,
877 /// # child : SomeError,
878 /// # }
879 /// # impl Display for HigherError {
880 /// # fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
881 /// # write!(f, "{}", self.msg)
882 /// # }
883 /// # }
884 /// # impl Error for HigherError {
885 /// # fn source(&self) -> Option<&(dyn 'static + Error)> {
886 /// # Some(&self.child)
887 /// # }
888 /// # }
889 /// #
890 /// #
891 /// #
892 /// let err = HigherError { msg: "Oh no, something went wrong!".into(), child: SomeError { msg: "A specific reason".into() } };
893 /// assert_eq!(err.trace_colored().to_string(), r#"Oh no, something went wrong!
894 ///
895 /// Caused by:
896 /// o A specific reason
897 ///
898 /// "#);
899 #[cfg(feature = "colors")]
900 #[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
901 fn trace_colored(&self) -> ErrorTraceColorFormatter;
902}
903impl<T: ?Sized + Error> ErrorTrace for T {
904 #[inline]
905 fn freeze(&self) -> FrozenTrace { FrozenTrace::new(self) }
906
907 #[inline]
908 fn trace(&self) -> ErrorTraceFormatter { ErrorTraceFormatter { msg: Cow::Owned(self.to_string()), err: self.source() } }
909
910 #[cfg(feature = "colors")]
911 #[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
912 #[inline]
913 fn trace_colored(&self) -> ErrorTraceColorFormatter { ErrorTraceColorFormatter { msg: Cow::Owned(self.to_string()), err: self.source() } }
914}