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}