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//!   ![Showing the same error as above but with some errors](https://github.com/Lut99/error-trace-rs/raw/main/img/example_colors.png)
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}