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 query_invariant(message: impl Into<String>) -> Self {
58 Self::new(
59 ErrorClass::InvariantViolation,
60 ErrorOrigin::Query,
61 message.into(),
62 )
63 }
64
65 pub(crate) fn index_invariant(message: impl Into<String>) -> Self {
67 Self::new(
68 ErrorClass::InvariantViolation,
69 ErrorOrigin::Index,
70 message.into(),
71 )
72 }
73
74 pub(crate) fn executor_invariant(message: impl Into<String>) -> Self {
76 Self::new(
77 ErrorClass::InvariantViolation,
78 ErrorOrigin::Executor,
79 message.into(),
80 )
81 }
82
83 pub(crate) fn store_invariant(message: impl Into<String>) -> Self {
85 Self::new(
86 ErrorClass::InvariantViolation,
87 ErrorOrigin::Store,
88 message.into(),
89 )
90 }
91
92 pub(crate) fn corruption(origin: ErrorOrigin, message: impl Into<String>) -> Self {
94 Self::new(ErrorClass::Corruption, origin, message.into())
95 }
96
97 pub(crate) fn store_internal(message: impl Into<String>) -> Self {
99 Self::new(ErrorClass::Internal, ErrorOrigin::Store, message.into())
100 }
101
102 pub(crate) fn executor_internal(message: impl Into<String>) -> Self {
104 Self::new(ErrorClass::Internal, ErrorOrigin::Executor, message.into())
105 }
106
107 pub(crate) fn index_internal(message: impl Into<String>) -> Self {
109 Self::new(ErrorClass::Internal, ErrorOrigin::Index, message.into())
110 }
111
112 pub(crate) fn serialize_internal(message: impl Into<String>) -> Self {
114 Self::new(ErrorClass::Internal, ErrorOrigin::Serialize, message.into())
115 }
116
117 pub(crate) fn store_corruption(message: impl Into<String>) -> Self {
119 Self::new(ErrorClass::Corruption, ErrorOrigin::Store, message.into())
120 }
121
122 pub(crate) fn index_corruption(message: impl Into<String>) -> Self {
124 Self::new(ErrorClass::Corruption, ErrorOrigin::Index, message.into())
125 }
126
127 pub(crate) fn serialize_corruption(message: impl Into<String>) -> Self {
129 Self::new(
130 ErrorClass::Corruption,
131 ErrorOrigin::Serialize,
132 message.into(),
133 )
134 }
135
136 pub(crate) fn store_unsupported(message: impl Into<String>) -> Self {
138 Self::new(ErrorClass::Unsupported, ErrorOrigin::Store, message.into())
139 }
140
141 pub(crate) fn index_unsupported(message: impl Into<String>) -> Self {
143 Self::new(ErrorClass::Unsupported, ErrorOrigin::Index, message.into())
144 }
145
146 pub(crate) fn executor_unsupported(message: impl Into<String>) -> Self {
148 Self::new(
149 ErrorClass::Unsupported,
150 ErrorOrigin::Executor,
151 message.into(),
152 )
153 }
154
155 pub(crate) fn serialize_unsupported(message: impl Into<String>) -> Self {
157 Self::new(
158 ErrorClass::Unsupported,
159 ErrorOrigin::Serialize,
160 message.into(),
161 )
162 }
163
164 pub fn store_not_found(key: impl Into<String>) -> Self {
165 let key = key.into();
166
167 Self {
168 class: ErrorClass::NotFound,
169 origin: ErrorOrigin::Store,
170 message: format!("data key not found: {key}"),
171 detail: Some(ErrorDetail::Store(StoreError::NotFound { key })),
172 }
173 }
174
175 pub fn unsupported_entity_path(path: impl Into<String>) -> Self {
177 let path = path.into();
178
179 Self::new(
180 ErrorClass::Unsupported,
181 ErrorOrigin::Store,
182 format!("unsupported entity path: '{path}'"),
183 )
184 }
185
186 #[must_use]
187 pub const fn is_not_found(&self) -> bool {
188 matches!(
189 self.detail,
190 Some(ErrorDetail::Store(StoreError::NotFound { .. }))
191 )
192 }
193
194 #[must_use]
195 pub fn display_with_class(&self) -> String {
196 format!("{}:{}: {}", self.origin, self.class, self.message)
197 }
198
199 pub(crate) fn index_plan_corruption(origin: ErrorOrigin, message: impl Into<String>) -> Self {
201 let message = message.into();
202 Self::new(
203 ErrorClass::Corruption,
204 origin,
205 format!("corruption detected ({origin}): {message}"),
206 )
207 }
208
209 pub(crate) fn index_violation(path: &str, index_fields: &[&str]) -> Self {
211 Self::new(
212 ErrorClass::Conflict,
213 ErrorOrigin::Index,
214 format!(
215 "index constraint violation: {path} ({})",
216 index_fields.join(", ")
217 ),
218 )
219 }
220
221 pub(crate) fn from_cursor_plan_error(err: PlanError) -> Self {
223 let message = match &err {
224 PlanError::Cursor(inner) => match inner.as_ref() {
225 CursorPlanError::ContinuationCursorBoundaryArityMismatch { expected: 1, found } => {
226 format!(
227 "executor invariant violated: pk-ordered continuation boundary must contain exactly 1 slot, found {found}"
228 )
229 }
230 CursorPlanError::ContinuationCursorPrimaryKeyTypeMismatch {
231 value: None, ..
232 } => "executor invariant violated: pk cursor slot must be present".to_string(),
233 CursorPlanError::ContinuationCursorPrimaryKeyTypeMismatch {
234 value: Some(_),
235 ..
236 } => "executor invariant violated: pk cursor slot type mismatch".to_string(),
237 _ => err.to_string(),
238 },
239 _ => err.to_string(),
240 };
241
242 Self::query_invariant(message)
243 }
244
245 pub(crate) fn from_executor_plan_error(err: PlanError) -> Self {
247 Self::query_invariant(err.to_string())
248 }
249}
250
251#[derive(Debug, ThisError)]
259pub enum ErrorDetail {
260 #[error("{0}")]
261 Store(StoreError),
262 #[error("{0}")]
263 ViewPatch(crate::patch::MergePatchError),
264 }
274
275impl From<MergePatchError> for InternalError {
276 fn from(err: MergePatchError) -> Self {
277 Self {
278 class: ErrorClass::Unsupported,
279 origin: ErrorOrigin::Interface,
280 message: err.to_string(),
281 detail: Some(ErrorDetail::ViewPatch(err)),
282 }
283 }
284}
285
286#[derive(Debug, ThisError)]
294pub enum StoreError {
295 #[error("key not found: {key}")]
296 NotFound { key: String },
297
298 #[error("store corruption: {message}")]
299 Corrupt { message: String },
300
301 #[error("store invariant violation: {message}")]
302 InvariantViolation { message: String },
303}
304
305#[derive(Clone, Copy, Debug, Eq, PartialEq)]
312pub enum ErrorClass {
313 Corruption,
314 NotFound,
315 Internal,
316 Conflict,
317 Unsupported,
318 InvariantViolation,
319}
320
321impl fmt::Display for ErrorClass {
322 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
323 let label = match self {
324 Self::Corruption => "corruption",
325 Self::NotFound => "not_found",
326 Self::Internal => "internal",
327 Self::Conflict => "conflict",
328 Self::Unsupported => "unsupported",
329 Self::InvariantViolation => "invariant_violation",
330 };
331 write!(f, "{label}")
332 }
333}
334
335#[derive(Clone, Copy, Debug, Eq, PartialEq)]
342pub enum ErrorOrigin {
343 Serialize,
344 Store,
345 Index,
346 Query,
347 Response,
348 Executor,
349 Interface,
350}
351
352impl fmt::Display for ErrorOrigin {
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 let label = match self {
355 Self::Serialize => "serialize",
356 Self::Store => "store",
357 Self::Index => "index",
358 Self::Query => "query",
359 Self::Response => "response",
360 Self::Executor => "executor",
361 Self::Interface => "interface",
362 };
363 write!(f, "{label}")
364 }
365}