surrealdb_common/error/leaf.rs
1//! The contract a SurrealDB error type implements to reach the wire.
2//!
3//! Each layer of the engine owns its own error enum, but they all have to
4//! arrive at the client as one [`surrealdb_types::Error`]. [`LeafError`] is
5//! that conversion, stated once so every layer answers the same question the
6//! same way.
7
8use surrealdb_types::Error as TypesError;
9
10/// Converts an error into its public form.
11///
12/// Implement [`map_kind`](LeafError::map_kind) and nothing else.
13///
14/// # Nesting
15///
16/// When one error wraps another, the outer arm must call the inner error's
17/// `map_kind`, **not** its `to_types_error`. `to_types_error` attaches the
18/// cause, so calling it inward attaches one at each level and the outer
19/// attachment silently replaces the inner one. `map_kind` classifies without
20/// touching the cause, which is why it is the only method to write and
21/// `to_types_error` is provided.
22pub trait LeafError: std::error::Error + Sized + Send + Sync + 'static {
23 /// Classify this error: kind, details and code.
24 ///
25 /// `message` is the error's own `Display` output, computed once by
26 /// [`to_types_error`](LeafError::to_types_error) and passed in so an
27 /// implementation can move payload fields out of `self` rather than
28 /// cloning them to build its message.
29 ///
30 /// Do not attach a cause here.
31 fn map_kind(self, message: String) -> TypesError;
32
33 /// Produce the public error.
34 ///
35 /// Frames [`map_kind`](LeafError::map_kind) with the error's message and
36 /// one level of its source chain. Deeper levels are deliberately dropped:
37 /// they routinely name storage internals and raw field values, which are
38 /// not for clients.
39 fn to_types_error(self) -> TypesError {
40 let message = self.to_string();
41 let cause = std::error::Error::source(&self).map(|s| TypesError::internal(s.to_string()));
42 let mapped = self.map_kind(message);
43 match cause {
44 Some(cause) => mapped.with_cause(cause),
45 None => mapped,
46 }
47 }
48}
49
50/// A variant that reaches clients as an untyped internal error only because it
51/// always has.
52///
53/// Behaviourally identical to [`TypesError::internal`]. It exists to keep those
54/// variants greppable and countable while they are worked off, so that a
55/// deliberate `internal` and an unclassified one do not look alike.
56#[inline]
57pub fn internal_todo(message: String) -> TypesError {
58 TypesError::internal(message)
59}