1use crate::{
2 db::query::plan::{CursorPlanError, PlanError},
3 patch::MergePatchError,
4};
5use std::fmt;
6use thiserror::Error as ThisError;
7
8#[derive(Debug, ThisError)]
16#[error("{message}")]
17pub struct InternalError {
18 pub class: ErrorClass,
19 pub origin: ErrorOrigin,
20 pub message: String,
21
22 pub detail: Option<ErrorDetail>,
25}
26
27impl InternalError {
28 pub fn new(class: ErrorClass, origin: ErrorOrigin, message: impl Into<String>) -> Self {
32 let message = message.into();
33
34 let detail = match (class, origin) {
35 (ErrorClass::Corruption, ErrorOrigin::Store) => {
36 Some(ErrorDetail::Store(StoreError::Corrupt {
37 message: message.clone(),
38 }))
39 }
40 (ErrorClass::InvariantViolation, ErrorOrigin::Store) => {
41 Some(ErrorDetail::Store(StoreError::InvariantViolation {
42 message: message.clone(),
43 }))
44 }
45 _ => None,
46 };
47
48 Self {
49 class,
50 origin,
51 message,
52 detail,
53 }
54 }
55
56 pub(crate) fn classified(
58 class: ErrorClass,
59 origin: ErrorOrigin,
60 message: impl Into<String>,
61 ) -> Self {
62 Self::new(class, origin, message)
63 }
64
65 pub(crate) fn with_message(self, message: impl Into<String>) -> Self {
67 Self::classified(self.class, self.origin, message)
68 }
69
70 pub(crate) fn query_invariant(message: impl Into<String>) -> Self {
72 Self::new(
73 ErrorClass::InvariantViolation,
74 ErrorOrigin::Query,
75 message.into(),
76 )
77 }
78
79 #[must_use]
81 pub(crate) fn executor_invariant_message(reason: impl Into<String>) -> String {
82 format!("executor invariant violated: {}", reason.into())
83 }
84
85 #[must_use]
87 pub(crate) fn invalid_logical_plan_message(reason: impl Into<String>) -> String {
88 format!("invalid logical plan: {}", reason.into())
89 }
90
91 pub(crate) fn query_executor_invariant(reason: impl Into<String>) -> Self {
93 Self::query_invariant(Self::executor_invariant_message(reason))
94 }
95
96 pub(crate) fn query_invalid_logical_plan(reason: impl Into<String>) -> Self {
98 Self::query_invariant(Self::invalid_logical_plan_message(reason))
99 }
100
101 pub(crate) fn index_invariant(message: impl Into<String>) -> Self {
103 Self::new(
104 ErrorClass::InvariantViolation,
105 ErrorOrigin::Index,
106 message.into(),
107 )
108 }
109
110 pub(crate) fn executor_invariant(message: impl Into<String>) -> Self {
112 Self::new(
113 ErrorClass::InvariantViolation,
114 ErrorOrigin::Executor,
115 message.into(),
116 )
117 }
118
119 pub(crate) fn store_invariant(message: impl Into<String>) -> Self {
121 Self::new(
122 ErrorClass::InvariantViolation,
123 ErrorOrigin::Store,
124 message.into(),
125 )
126 }
127
128 pub(crate) fn store_internal(message: impl Into<String>) -> Self {
130 Self::new(ErrorClass::Internal, ErrorOrigin::Store, message.into())
131 }
132
133 pub(crate) fn executor_internal(message: impl Into<String>) -> Self {
135 Self::new(ErrorClass::Internal, ErrorOrigin::Executor, message.into())
136 }
137
138 pub(crate) fn index_internal(message: impl Into<String>) -> Self {
140 Self::new(ErrorClass::Internal, ErrorOrigin::Index, message.into())
141 }
142
143 #[cfg(test)]
145 pub(crate) fn query_internal(message: impl Into<String>) -> Self {
146 Self::new(ErrorClass::Internal, ErrorOrigin::Query, message.into())
147 }
148
149 pub(crate) fn serialize_internal(message: impl Into<String>) -> Self {
151 Self::new(ErrorClass::Internal, ErrorOrigin::Serialize, message.into())
152 }
153
154 pub(crate) fn store_corruption(message: impl Into<String>) -> Self {
156 Self::new(ErrorClass::Corruption, ErrorOrigin::Store, message.into())
157 }
158
159 pub(crate) fn index_corruption(message: impl Into<String>) -> Self {
161 Self::new(ErrorClass::Corruption, ErrorOrigin::Index, message.into())
162 }
163
164 pub(crate) fn serialize_corruption(message: impl Into<String>) -> Self {
166 Self::new(
167 ErrorClass::Corruption,
168 ErrorOrigin::Serialize,
169 message.into(),
170 )
171 }
172
173 pub(crate) fn store_unsupported(message: impl Into<String>) -> Self {
175 Self::new(ErrorClass::Unsupported, ErrorOrigin::Store, message.into())
176 }
177
178 pub(crate) fn index_unsupported(message: impl Into<String>) -> Self {
180 Self::new(ErrorClass::Unsupported, ErrorOrigin::Index, message.into())
181 }
182
183 pub(crate) fn executor_unsupported(message: impl Into<String>) -> Self {
185 Self::new(
186 ErrorClass::Unsupported,
187 ErrorOrigin::Executor,
188 message.into(),
189 )
190 }
191
192 pub(crate) fn serialize_unsupported(message: impl Into<String>) -> Self {
194 Self::new(
195 ErrorClass::Unsupported,
196 ErrorOrigin::Serialize,
197 message.into(),
198 )
199 }
200
201 pub fn store_not_found(key: impl Into<String>) -> Self {
202 let key = key.into();
203
204 Self {
205 class: ErrorClass::NotFound,
206 origin: ErrorOrigin::Store,
207 message: format!("data key not found: {key}"),
208 detail: Some(ErrorDetail::Store(StoreError::NotFound { key })),
209 }
210 }
211
212 pub fn unsupported_entity_path(path: impl Into<String>) -> Self {
214 let path = path.into();
215
216 Self::new(
217 ErrorClass::Unsupported,
218 ErrorOrigin::Store,
219 format!("unsupported entity path: '{path}'"),
220 )
221 }
222
223 #[must_use]
224 pub const fn is_not_found(&self) -> bool {
225 matches!(
226 self.detail,
227 Some(ErrorDetail::Store(StoreError::NotFound { .. }))
228 )
229 }
230
231 #[must_use]
232 pub fn display_with_class(&self) -> String {
233 format!("{}:{}: {}", self.origin, self.class, self.message)
234 }
235
236 pub(crate) fn index_plan_corruption(origin: ErrorOrigin, message: impl Into<String>) -> Self {
238 let message = message.into();
239 Self::new(
240 ErrorClass::Corruption,
241 origin,
242 format!("corruption detected ({origin}): {message}"),
243 )
244 }
245
246 pub(crate) fn index_violation(path: &str, index_fields: &[&str]) -> Self {
248 Self::new(
249 ErrorClass::Conflict,
250 ErrorOrigin::Index,
251 format!(
252 "index constraint violation: {path} ({})",
253 index_fields.join(", ")
254 ),
255 )
256 }
257
258 pub(crate) fn from_cursor_plan_error(err: PlanError) -> Self {
260 let message = match &err {
261 PlanError::Cursor(inner) => match inner.as_ref() {
262 CursorPlanError::ContinuationCursorBoundaryArityMismatch { expected: 1, found } => {
263 Self::executor_invariant_message(format!(
264 "pk-ordered continuation boundary must contain exactly 1 slot, found {found}"
265 ))
266 }
267 CursorPlanError::ContinuationCursorPrimaryKeyTypeMismatch {
268 value: None, ..
269 } => Self::executor_invariant_message("pk cursor slot must be present"),
270 CursorPlanError::ContinuationCursorPrimaryKeyTypeMismatch {
271 value: Some(_),
272 ..
273 } => Self::executor_invariant_message("pk cursor slot type mismatch"),
274 _ => err.to_string(),
275 },
276 _ => err.to_string(),
277 };
278
279 Self::query_invariant(message)
280 }
281
282 pub(crate) fn from_executor_plan_error(err: PlanError) -> Self {
284 Self::query_invariant(err.to_string())
285 }
286}
287
288#[derive(Debug, ThisError)]
296pub enum ErrorDetail {
297 #[error("{0}")]
298 Store(StoreError),
299 #[error("{0}")]
300 ViewPatch(crate::patch::MergePatchError),
301 }
311
312impl From<MergePatchError> for InternalError {
313 fn from(err: MergePatchError) -> Self {
314 Self {
315 class: ErrorClass::Unsupported,
316 origin: ErrorOrigin::Interface,
317 message: err.to_string(),
318 detail: Some(ErrorDetail::ViewPatch(err)),
319 }
320 }
321}
322
323#[derive(Debug, ThisError)]
331pub enum StoreError {
332 #[error("key not found: {key}")]
333 NotFound { key: String },
334
335 #[error("store corruption: {message}")]
336 Corrupt { message: String },
337
338 #[error("store invariant violation: {message}")]
339 InvariantViolation { message: String },
340}
341
342#[derive(Clone, Copy, Debug, Eq, PartialEq)]
349pub enum ErrorClass {
350 Corruption,
351 NotFound,
352 Internal,
353 Conflict,
354 Unsupported,
355 InvariantViolation,
356}
357
358impl fmt::Display for ErrorClass {
359 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360 let label = match self {
361 Self::Corruption => "corruption",
362 Self::NotFound => "not_found",
363 Self::Internal => "internal",
364 Self::Conflict => "conflict",
365 Self::Unsupported => "unsupported",
366 Self::InvariantViolation => "invariant_violation",
367 };
368 write!(f, "{label}")
369 }
370}
371
372#[derive(Clone, Copy, Debug, Eq, PartialEq)]
379pub enum ErrorOrigin {
380 Serialize,
381 Store,
382 Index,
383 Query,
384 Response,
385 Executor,
386 Interface,
387}
388
389impl fmt::Display for ErrorOrigin {
390 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391 let label = match self {
392 Self::Serialize => "serialize",
393 Self::Store => "store",
394 Self::Index => "index",
395 Self::Query => "query",
396 Self::Response => "response",
397 Self::Executor => "executor",
398 Self::Interface => "interface",
399 };
400 write!(f, "{label}")
401 }
402}