1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
use super::source::{DisplayOnly, DisplayError, ErrorSource, StdError};
use core::fmt::Display;

/// An `ErrorMessageTracer` can be used to generically trace
/// any error detail that implements [`Display`](std::fmt::Display).
///
/// The error tracer may add backtrace information when the tracing
/// methods are called. However since the error detail is required
/// to only implement `Display`, any existing error trace may be
/// lost even if the error detail implements `Error` and contains
/// backtrace, unless the backtrace is serialized in `Display`.
pub trait ErrorMessageTracer {
    /// Creates a new error trace, starting from a source error
    /// detail that implements [`Display`](std::fmt::Display).
    fn new_message<E: Display>(message: &E) -> Self;

    /// Adds new error detail to an existing trace.
    fn add_message<E: Display>(self, message: &E) -> Self;

    /// If the `std` feature is enabled, the error tracer
    /// also provides method to optionally converts itself
    /// to a `dyn` [`Error`](std::error::Error).
    #[cfg(feature = "std")]
    fn as_error(&self) -> Option<&(dyn std::error::Error + 'static)>;
}

/// An error tracer implements `ErrorTracer<E>` if it supports
/// more sophisticated error tracing for an error type `E`.
/// The contraint for `E` depends on the specific error tracer
/// implementation.
///
/// For example, [`EyreTracer`](crate::tracer_impl::eyre::EyreTracer)
/// and [`AnyhowTracer`](crate::tracer_impl::anyhow::AnyhowTracer) requires
/// an error type to satisfy `E: Error + Send + Sync + 'static`.
///
/// The error tracer also requires ownership of the source error to be
/// transferred to the error tracer. Because of this, it may not be possible
/// to extract a source error type to be used as both error detail and
/// error trace. We also should not expect `E` to implement `Clone`, as
/// error types such as [`eyre::Report`] do not implement `Clone`.
///
/// Note that because of restrictions on generic trait implementations outside
/// of a Rust crate, it is not possible to generically implement `ErrorTracer<E>`
/// for a custom error tracer outside of `flex-error`. If you want to define custom
/// error tracers, you may need to make a copy of your own definition of
/// `ErrorTracer` and also [`StdError`] in your own crate.
pub trait ErrorTracer<E>: ErrorMessageTracer {
    /// Create a new error trace from `E`, also taking ownership of it.
    ///
    /// This calls the underlying methods such as [`eyre::Report::new`]
    /// and [`anyhow::Error::new`].
    fn new_trace(err: E) -> Self;

    /// Add a new error trace from `E`. In the current underlying implementation,
    /// this is effectively still has the same behavior as
    /// [`ErrorMessageTracer::add_message`]. This is because [`eyre`] and
    /// [`anyhow`] do not support adding new set of backtraces to an existing
    /// trace. So effectively, currently the error tracers can track at most
    /// one backtrace coming from the original error source.
    fn add_trace(self, err: E) -> Self;
}

impl<E, Tracer> ErrorSource<Tracer> for DisplayError<E>
where
    E: Display,
    Tracer: ErrorMessageTracer,
{
    type Detail = E;
    type Source = E;

    fn error_details(source: Self::Source) -> (Self::Detail, Option<Tracer>) {
        let trace = Tracer::new_message(&source);
        (source, Some(trace))
    }
}

impl<E, Tracer> ErrorSource<Tracer> for DisplayOnly<E>
where
    E: Display,
    Tracer: ErrorMessageTracer,
{
    type Detail = ();
    type Source = E;

    fn error_details(source: Self::Source) -> (Self::Detail, Option<Tracer>) {
        let trace = Tracer::new_message(&source);
        ((), Some(trace))
    }
}

impl<E, Tracer> ErrorSource<Tracer> for StdError<E>
where
    Tracer: ErrorTracer<E>,
{
    type Detail = ();
    type Source = E;

    fn error_details(source: Self::Source) -> (Self::Detail, Option<Tracer>) {
        let trace = Tracer::new_trace(source);
        ((), Some(trace))
    }
}