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//! 
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}