error_trace/
lib.rs

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