flex_error/
source.rs

1use core::fmt::Display;
2use core::marker::PhantomData;
3
4use crate::tracer::{ErrorMessageTracer, ErrorTracer};
5
6/**
7 A type implementing `ErrorSource<Trace>` is a proxy type that provides the
8 capability of extracting from an error source of type `Self::Source`,
9 returning error detail of type `Self::Detail`, and an optional error
10 tracer of type `Tracer`.
11
12 The proxy type `Self` is not used anywhere. We separate out `Self`
13 and `Self::Source` so that there can be different generic implementations
14 of error sources, such as for all `E: Display` or for all `E: Error`.
15
16 There are currently 4 types of error sources:
17   - [`NoSource`] - Indicating the lack of any error source
18   - [`DisplayError`] - An error source that implements [`Display`](std::fmt::Display)
19     to be used for tracing, and also be stored as detail.
20   - [`DisplayOnly`] - An error source that implements [`Display`](std::fmt::Display)
21     to be used for tracing, and discarded instead of being stored as detail.
22   - [`DetailOnly`] - An error source that is used as detail and do not contain any error trace.
23   - [`TraceError`] - An error source that implements [`Error`](std::error::Error)
24     and used only for tracing.
25   - [`TraceClone`] - An error source that implements [`Error`](std::error::Error) and
26     have a cloned copy as detail.
27**/
28
29pub trait ErrorSource<Trace> {
30    /// The type of the error source.
31    type Source;
32
33    /// The type of the error detail that can be extracted from the error source
34    type Detail;
35
36    /// Extracts the error details out from the error source, together with
37    /// an optional error trace.
38    fn error_details(source: Self::Source) -> (Self::Detail, Option<Trace>);
39}
40
41/// Type alias to `<Error as ErrorSource<Trace>>::Detail`
42pub type AsErrorDetail<Error, Trace> = <Error as ErrorSource<Trace>>::Detail;
43
44/// Type alias to `<Error as ErrorSource<Trace>>::Source`
45pub type AsErrorSource<Error, Trace> = <Error as ErrorSource<Trace>>::Source;
46
47/// An [`ErrorSource`] that can be used to represent to lack of any error source.
48/// Both its `Source` and `Detail` types are `()`. This can be used for primitive errors
49/// that are not caused by any error source.
50///
51/// In practice, it is also possible to omit specifying any error source inside
52/// [`define_error!`](crate::define_error), which has similar effect as using
53/// `NoSource` but with the `source` field omitted entirely.
54pub struct NoSource;
55
56/// An [`ErrorSource`] that implements [`Display`](std::fmt::Display) and
57/// can be traced by error tracers implementing [`ErrorMessageTracer`](crate::tracer::ErrorMessageTracer).
58///
59/// Both its `Source` and `Detail` types are `E`. When extraced, it also provides
60/// an error trace that is traced from its string representation.
61pub struct DisplayError<E>(PhantomData<E>);
62
63pub struct DisplayOnly<E>(PhantomData<E>);
64
65/// An [`ErrorSource`] that should implement [`Error`](std::error::Error) and
66/// other constraints such as `Send`, `Sync`, `'static`, so that it can be traced
67/// by error tracing libraries such as [`eyre`] and [`anyhow`]. Because these libraries
68/// take ownership of the source error object, the error cannot be extracted as detail
69/// at the same time.
70pub struct TraceError<E>(PhantomData<E>);
71
72pub struct TraceClone<E>(PhantomData<E>);
73
74/// An [`ErrorSource`] that contains only the error trace with no detail.
75/// This can for example be used for upstream functions that return tracers like
76/// [`eyre::Report`] directly.
77pub struct TraceOnly<Tracer>(PhantomData<Tracer>);
78
79/// An [`ErrorSource`] that only provides error details but do not provide any trace.
80/// This can typically comes from primitive error types that do not implement
81/// [`Error`](std::error::Error). The `Detail` type is the error and the returned
82/// trace is `None`.
83///
84/// It is also possible to omit specifying the error as an error source, and instead
85/// place it as a field in the error variant. However specifying it as a `DetailOnly`
86/// source may give stronger hint to the reader that the particular error variant
87/// is caused by other underlying errors.
88pub struct DetailOnly<Detail>(PhantomData<Detail>);
89
90pub struct BoxDetail<Detail: ?Sized>(PhantomData<Detail>);
91
92impl<Detail, Trace> ErrorSource<Trace> for DetailOnly<Detail> {
93    type Detail = Detail;
94    type Source = Detail;
95
96    fn error_details(source: Self::Source) -> (Self::Detail, Option<Trace>) {
97        (source, None)
98    }
99}
100
101impl<Trace> ErrorSource<Trace> for NoSource {
102    type Detail = ();
103    type Source = ();
104
105    fn error_details(_: Self::Source) -> (Self::Detail, Option<Trace>) {
106        ((), None)
107    }
108}
109
110impl<Trace> ErrorSource<Trace> for TraceOnly<Trace> {
111    type Detail = ();
112    type Source = Trace;
113
114    fn error_details(source: Self::Source) -> (Self::Detail, Option<Trace>) {
115        ((), Some(source))
116    }
117}
118
119impl<E, Tracer> ErrorSource<Tracer> for DisplayError<E>
120where
121    E: Display,
122    Tracer: ErrorMessageTracer,
123{
124    type Detail = E;
125    type Source = E;
126
127    fn error_details(source: Self::Source) -> (Self::Detail, Option<Tracer>) {
128        let trace = Tracer::new_message(&source);
129        (source, Some(trace))
130    }
131}
132
133impl<E, Tracer> ErrorSource<Tracer> for DisplayOnly<E>
134where
135    E: Display,
136    Tracer: ErrorMessageTracer,
137{
138    type Detail = ();
139    type Source = E;
140
141    fn error_details(source: Self::Source) -> (Self::Detail, Option<Tracer>) {
142        let trace = Tracer::new_message(&source);
143        ((), Some(trace))
144    }
145}
146
147impl<E, Tracer> ErrorSource<Tracer> for TraceClone<E>
148where
149    E: Clone,
150    Tracer: ErrorTracer<E>,
151{
152    type Detail = E;
153    type Source = E;
154
155    fn error_details(source: Self::Source) -> (Self::Detail, Option<Tracer>) {
156        let detail = source.clone();
157        let trace = Tracer::new_trace(source);
158        (detail, Some(trace))
159    }
160}
161
162impl<E, Tracer> ErrorSource<Tracer> for TraceError<E>
163where
164    Tracer: ErrorTracer<E>,
165{
166    type Detail = ();
167    type Source = E;
168
169    fn error_details(source: Self::Source) -> (Self::Detail, Option<Tracer>) {
170        let trace = Tracer::new_trace(source);
171        ((), Some(trace))
172    }
173}