derail/error.rs
1//! The [`Error`] and [`ErrorExt`] traits and related items.
2
3use core::{error::Error as CoreError, fmt, iter, ops::ControlFlow};
4
5use crate::{VisitContext, Visitor, VisitorExt as _};
6
7mod ext;
8#[cfg(feature = "alloc")]
9mod impls_alloc;
10mod impls_core;
11
12pub use ext::ErrorExt;
13
14/// Trait for types that represent an error condition.
15///
16/// # Implementing
17///
18/// In order for [`Visitor`]s and [`Error`]s to work correctly, [`Error`]s must
19/// follow several rules in their implementations of various trait methods. It
20/// is recommended to use the derive macro to implement these trait methods, as
21/// it will ensure these rules are followed.
22///
23/// It is considered a logic error to implement these trait methods such that
24/// these rules are violated. Therefore, `unsafe` code must not rely on the
25/// correctness of implementations of this trait.
26///
27/// The rules for each trait method are described below.
28///
29/// ## [`Debug::fmt`]
30///
31/// There are no additional requirements on the implementation of this method.
32///
33/// ## [`Display::fmt`]
34///
35/// If this [`Error`] value will be [skipped], its implementation of
36/// [`Display::fmt`] must forward to the child, which will follow these rules
37/// itself. Otherwise, the below rules must be followed.
38///
39/// The text written should:
40///
41/// * Describe the error condition.
42/// * Be concise.
43/// * Start with a lowercase letter, except in certain situations, such as if an
44/// initialism or acronym appears at the start of the text.
45///
46/// The text written should not:
47///
48/// * Contain trailing punctuation.
49/// * Contain newlines.
50/// * Describe the error condition(s) that caused this error condition, if any.
51/// Instead, such information should be provided by implementing
52/// [`Error::accept`] such that these error conditions are visited as children
53/// of this [`Error`].
54///
55/// ## [`Error::accept`]
56///
57/// If any [`Visitor`] method returns [`ControlFlow::Break`], further calls to
58/// [`Visitor`] methods must not be made for the remainder of the current method
59/// call. This also applies to [`VisitorExt`] methods that call [`Visitor`]
60/// methods internally.
61///
62/// Each [`Error`] value must implement exactly one of the following behaviors.
63/// Typically, the behavior is chosen on a per-`struct` and per-`enum`-variant
64/// basis. The chosen behavior must match that of [`Error::details`].
65///
66/// ### Exactly zero children
67///
68/// 1. Call [`Visitor::visit`], passing `self` to `visitee` and `ctx` to `ctx`.
69///
70/// ### Zero or more children
71///
72/// 1. Call [`Visitor::visit`], passing `self` to `visitee` and `ctx` to `ctx`.
73/// 2. If `self` has at least one child:
74/// 1. Call [`Visitor::push`].
75/// 2. Call [`VisitorExt::visit_many`] or [`VisitorExt::visit_map_many`],
76/// passing the children to `errors`.
77/// 3. Call [`Visitor::pop`].
78///
79/// ### Exactly one child, skipping `self`
80///
81/// 1. Call [`VisitorExt::visit_many`] or [`VisitorExt::visit_map_many`],
82/// passing a single-item iterator yielding the child to `errors`.
83///
84/// In this situation, `self`'s [`Display::fmt`] implementation must forward to
85/// the child's.
86///
87/// ### Exactly one child, skipping the child
88///
89/// 1. Call [`Visitor::visit`], passing `self` to `visitee` and `ctx` to `ctx`.
90/// 2. If the child has at least one child ([`ErrorExt::has_children`] can be
91/// used for detecting this case):
92/// 1. Call [`Visitor::push`].
93/// 2. Call [`VisitorExt::visit_children_of`] or
94/// [`VisitorExt::visit_map_children_of`], passing the child to `error`.
95/// 3. Call [`Visitor::pop`].
96///
97/// ## [`Error::details`]
98///
99/// Each [`Error`] value must implement exactly one of the following behaviors.
100/// Typically, the behavior is chosen on a per-`struct` and per-`enum`-variant
101/// basis. The chosen behavior must match that of [`Error::accept`].
102///
103/// ### Exactly zero children
104///
105/// 1. Return `self`'s details.
106///
107/// ### Zero or more children
108///
109/// 1. Return `self`'s details.
110///
111/// ### Exactly one child, skipping `self`
112///
113/// 1. If [`VisitorExt::visit_many`] is used for this child in the corresponding
114/// [`Error::accept`] implementation, return the value obtained by calling
115/// [`Error::details`] on the child. If [`VisitorExt::visit_map_many`] is
116/// used for this child in the corresponding [`Error::accept`]
117/// implementation, call [`Error::details`] on the child and pass the value
118/// to the function passed to `map_details` in the
119/// [`VisitorExt::visit_map_many`] call and return the resulting value.
120///
121/// ### Exactly one child, skipping the child
122///
123/// 1. Return `self`'s details.
124///
125/// [`Debug::fmt`]: fmt::Debug::fmt
126/// [`Display::fmt`]: fmt::Display::fmt
127/// [`Display`]: fmt::Display
128/// [`VisitorExt::visit_children_of`]: crate::VisitorExt::visit_children_of
129/// [`VisitorExt::visit_many`]: crate::VisitorExt::visit_many
130/// [`VisitorExt::visit_map_children_of`]: crate::VisitorExt::visit_map_children_of
131/// [`VisitorExt::visit_map_many`]: crate::VisitorExt::visit_map_many
132/// [`VisitorExt`]: crate::VisitorExt
133/// [skipped]: Error#exactly-one-child-skipping-self
134pub trait Error: fmt::Debug + fmt::Display {
135 /// Type that contains details about this error.
136 ///
137 /// This can be used for providing additional information about an [`Error`]
138 /// such as backtraces, help text, application-specific error codes, and
139 /// so on.
140 ///
141 /// For [`Error`]s that have no children, consider making a backtrace
142 /// available through [`Error::details`]. Note that a backtrace should be
143 /// captured when an [`Error`] is created, not when [`Error::details`] is
144 /// called: the former would point to the location where the [`Error`] was
145 /// *caused*, and the latter would point to the location where the [`Error`]
146 /// was *being inspected*.
147 ///
148 /// # Design shortcomings
149 ///
150 /// Ideally, this would be generic over the lifetime of `self` so that
151 /// values returned from [`details`](Error::details) had the *option* of
152 /// borrowing from `self`. Unfortunately, [traits with generic associated
153 /// types are not `dyn`-compatible at the moment][0], and this trait is
154 /// designed to be `dyn`-compatible. In the meantime, try to choose or
155 /// create concrete types for this associated type whose contents are
156 /// [`Copy`] or cheap to [`Clone`] (e.g. via reference-counting pointers).
157 ///
158 /// [0]: https://github.com/rust-lang/rust/issues/81823
159 type Details;
160
161 /// Accept a [`Visitor`], causing it to traverse `self`'s tree.
162 fn accept(
163 &self,
164 visitor: &mut dyn Visitor<Details = Self::Details>,
165 ctx: VisitContext<'_, Self::Details>,
166 ) -> ControlFlow<()>;
167
168 /// Get this [`Error`]'s details.
169 ///
170 /// See the documentation of [`Error::Details`] for more information.
171 fn details(&self) -> Self::Details;
172}
173
174/// Wraps a [`core::error::Error`] type so that it implements [`Error`].
175///
176/// This type's [`Error::accept`] implementation will visit `self` and its
177/// children by calling [`core::error::Error::source`] recursively, and its
178/// [`Display::fmt`] implementation directly calls `E`'s [`Display::fmt`]
179/// implementation.
180///
181/// [`Display::fmt`]: fmt::Display::fmt
182#[derive(Debug, Clone, Copy)]
183pub struct CoreCompat<E>(pub E);
184
185impl<E> fmt::Display for CoreCompat<E>
186where
187 E: fmt::Display,
188{
189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190 self.0.fmt(f)
191 }
192}
193
194impl<E> Error for CoreCompat<E>
195where
196 E: CoreError,
197{
198 type Details = ();
199
200 fn accept(
201 &self,
202 mut visitor: &mut dyn Visitor<Details = Self::Details>,
203 ctx: VisitContext<'_, Self::Details>,
204 ) -> ControlFlow<()> {
205 visitor.visit(self, ctx)?;
206
207 if let Some(source) = self.0.source() {
208 visitor.push()?;
209 visitor.visit_many(iter::once(&CoreCompat(source)))?;
210 visitor.pop()?;
211 }
212
213 ControlFlow::Continue(())
214 }
215
216 fn details(&self) -> Self::Details {}
217}
218
219impl<E> From<E> for CoreCompat<E>
220where
221 E: CoreError,
222{
223 fn from(other: E) -> Self {
224 CoreCompat(other)
225 }
226}