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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use core::fmt::Display;
use core::marker::PhantomData;

use crate::tracer::{ErrorMessageTracer, ErrorTracer};

/// A type implementing `ErrorSource<Trace>` is a proxy type that provides the
/// capability of extracting from an error source of type `Self::Source`,
/// returning error detail of type `Self::Detail`, and an optional error
/// tracer of type `Tracer`.
///
/// The proxy type `Self` is not used anywhere. We separate out `Self`
/// and `Self::Source` so that there can be different generic implementations
/// of error sources, such as for all `E: Display` or for all `E: Error`.
///
/// There are currently 4 types of error sources:
///   - [`NoSource`] - Indicating the lack of any error source
///   - [`DisplayError`] - An error source that implements [`Display`](std::fmt::Display).
///   - [`DisplayOnly`] - An error source that implements [`Display`](std::fmt::Display) and do not provide additional detail.
///   - [`DetailOnly`] - An error source that do not contain any error trace
///   - [`TraceError`] - An error source that implements [`Error`](std::error::Error) with no detail.
///   - [`ErrorReport`](crate::report::ErrorReport) - An error type defined by `flex-error` that contains
///     both error details and error traces.

pub trait ErrorSource<Trace> {
    /// The type of the error source.
    type Source;

    /// The type of the error detail that can be extracted from the error source
    type Detail;

    /// Extracts the error details out from the error source, together with
    /// an optional error trace.
    fn error_details(source: Self::Source) -> (Self::Detail, Option<Trace>);
}

/// Type alias to `<Error as ErrorSource<Trace>>::Detail`
pub type AsErrorDetail<Error, Trace> = <Error as ErrorSource<Trace>>::Detail;

/// Type alias to `<Error as ErrorSource<Trace>>::Source`
pub type AsErrorSource<Error, Trace> = <Error as ErrorSource<Trace>>::Source;

/// An [`ErrorSource`] that can be used to represent to lack of any error source.
/// Both its `Source` and `Detail` types are `()`. This can be used for primitive errors
/// that are not caused by any error source.
///
/// In practice, it is also possible to omit specifying any error source inside
/// [`define_error!`](crate::define_error), which has similar effect as using
/// `NoSource` but with the `source` field omitted entirely.
pub struct NoSource;

/// An [`ErrorSource`] that implements [`Display`](std::fmt::Display) and
/// can be traced by error tracers implementing [`ErrorMessageTracer`](crate::tracer::ErrorMessageTracer).
///
/// Both its `Source` and `Detail` types are `E`. When extraced, it also provides
/// an error trace that is traced from its string representation.
pub struct DisplayError<E>(PhantomData<E>);

pub struct DisplayOnly<E>(PhantomData<E>);

/// An [`ErrorSource`] that should implement [`Error`](std::error::Error) and
/// other constraints such as `Send`, `Sync`, `'static`, so that it can be traced
/// by error tracing libraries such as [`eyre`] and [`anyhow`]. Because these libraries
/// take ownership of the source error object, the error cannot be extracted as detail
/// at the same time.
pub struct TraceError<E>(PhantomData<E>);

pub struct TraceClone<E>(PhantomData<E>);

/// An [`ErrorSource`] that contains only the error trace with no detail.
/// This can for example be used for upstream functions that return tracers like
/// [`eyre::Report`] directly.
///
/// Note that the `Tracer` type must be the same as the tracer type defined in
/// [`ErrorReport`](crate::ErrorReport), and most likely it should also be the same as
/// [`DefaultTracer`](crate::DefaultTracer).
/// If you plan to use `flex-error` with different feature flags, you should
/// classify the source as [`TraceError`] instead.
pub struct TraceOnly<Tracer>(PhantomData<Tracer>);

/// An [`ErrorSource`] that only provides error details but do not provide any trace.
/// This can typically comes from primitive error types that do not implement
/// [`Error`](std::error::Error). The `Detail` type is the error and the returned
/// trace is `None`.
///
/// It is also possible to omit specifying the error as an error source, and instead
/// place it as a field in the error variant. However specifying it as a `DetailOnly`
/// source may give stronger hint to the reader that the particular error variant
/// is caused by other underlying errors.
pub struct DetailOnly<Detail>(PhantomData<Detail>);

pub struct BoxDetail<Detail: ?Sized>(PhantomData<Detail>);

impl<Detail, Trace> ErrorSource<Trace> for DetailOnly<Detail> {
    type Detail = Detail;
    type Source = Detail;

    fn error_details(source: Self::Source) -> (Self::Detail, Option<Trace>) {
        (source, None)
    }
}

impl<Trace> ErrorSource<Trace> for NoSource {
    type Detail = ();
    type Source = ();

    fn error_details(_: Self::Source) -> (Self::Detail, Option<Trace>) {
        ((), None)
    }
}

impl<Trace> ErrorSource<Trace> for TraceOnly<Trace> {
    type Detail = ();
    type Source = Trace;

    fn error_details(source: Self::Source) -> (Self::Detail, Option<Trace>) {
        ((), Some(source))
    }
}

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 TraceClone<E>
where
    E: Clone,
    Tracer: ErrorTracer<E>,
{
    type Detail = E;
    type Source = E;

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

impl<E, Tracer> ErrorSource<Tracer> for TraceError<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))
    }
}