1#[cfg(test)]
8mod tests;
9
10use std::fmt;
11use thiserror::Error as ThisError;
12
13#[derive(Debug, ThisError)]
114#[error("{message}")]
115pub struct InternalError {
116 pub(crate) class: ErrorClass,
117 pub(crate) origin: ErrorOrigin,
118 pub(crate) message: String,
119
120 pub(crate) detail: Option<ErrorDetail>,
123}
124
125impl InternalError {
126 pub fn new(class: ErrorClass, origin: ErrorOrigin, message: impl Into<String>) -> Self {
130 let message = message.into();
131
132 let detail = match (class, origin) {
133 (ErrorClass::Corruption, ErrorOrigin::Store) => {
134 Some(ErrorDetail::Store(StoreError::Corrupt {
135 message: message.clone(),
136 }))
137 }
138 (ErrorClass::InvariantViolation, ErrorOrigin::Store) => {
139 Some(ErrorDetail::Store(StoreError::InvariantViolation {
140 message: message.clone(),
141 }))
142 }
143 _ => None,
144 };
145
146 Self {
147 class,
148 origin,
149 message,
150 detail,
151 }
152 }
153
154 #[must_use]
156 pub const fn class(&self) -> ErrorClass {
157 self.class
158 }
159
160 #[must_use]
162 pub const fn origin(&self) -> ErrorOrigin {
163 self.origin
164 }
165
166 #[must_use]
168 pub fn message(&self) -> &str {
169 &self.message
170 }
171
172 #[must_use]
174 pub const fn detail(&self) -> Option<&ErrorDetail> {
175 self.detail.as_ref()
176 }
177
178 #[must_use]
180 pub fn into_message(self) -> String {
181 self.message
182 }
183
184 pub(crate) fn classified(
186 class: ErrorClass,
187 origin: ErrorOrigin,
188 message: impl Into<String>,
189 ) -> Self {
190 Self::new(class, origin, message)
191 }
192
193 pub(crate) fn with_message(self, message: impl Into<String>) -> Self {
195 Self::classified(self.class, self.origin, message)
196 }
197
198 pub(crate) fn with_origin(self, origin: ErrorOrigin) -> Self {
202 Self::classified(self.class, origin, self.message)
203 }
204
205 pub(crate) fn index_invariant(message: impl Into<String>) -> Self {
207 Self::new(
208 ErrorClass::InvariantViolation,
209 ErrorOrigin::Index,
210 message.into(),
211 )
212 }
213
214 pub(crate) fn planner_executor_invariant(reason: impl Into<String>) -> Self {
217 Self::new(
218 ErrorClass::InvariantViolation,
219 ErrorOrigin::Planner,
220 Self::executor_invariant_message(reason),
221 )
222 }
223
224 pub(crate) fn query_executor_invariant(reason: impl Into<String>) -> Self {
227 Self::new(
228 ErrorClass::InvariantViolation,
229 ErrorOrigin::Query,
230 Self::executor_invariant_message(reason),
231 )
232 }
233
234 pub(crate) fn executor_invariant(message: impl Into<String>) -> Self {
236 Self::new(
237 ErrorClass::InvariantViolation,
238 ErrorOrigin::Executor,
239 message.into(),
240 )
241 }
242
243 pub(crate) fn executor_internal(message: impl Into<String>) -> Self {
245 Self::new(ErrorClass::Internal, ErrorOrigin::Executor, message.into())
246 }
247
248 pub(crate) fn executor_unsupported(message: impl Into<String>) -> Self {
250 Self::new(
251 ErrorClass::Unsupported,
252 ErrorOrigin::Executor,
253 message.into(),
254 )
255 }
256
257 #[must_use]
259 pub(crate) fn executor_invariant_message(reason: impl Into<String>) -> String {
260 format!("executor invariant violated: {}", reason.into())
261 }
262
263 pub(crate) fn planner_invariant(message: impl Into<String>) -> Self {
265 Self::new(
266 ErrorClass::InvariantViolation,
267 ErrorOrigin::Planner,
268 message.into(),
269 )
270 }
271
272 #[must_use]
274 pub(crate) fn invalid_logical_plan_message(reason: impl Into<String>) -> String {
275 format!("invalid logical plan: {}", reason.into())
276 }
277
278 pub(crate) fn query_invalid_logical_plan(reason: impl Into<String>) -> Self {
280 Self::planner_invariant(Self::invalid_logical_plan_message(reason))
281 }
282
283 pub(crate) fn query_invariant(message: impl Into<String>) -> Self {
285 Self::new(
286 ErrorClass::InvariantViolation,
287 ErrorOrigin::Query,
288 message.into(),
289 )
290 }
291
292 pub(crate) fn cursor_invariant(message: impl Into<String>) -> Self {
294 Self::new(
295 ErrorClass::InvariantViolation,
296 ErrorOrigin::Cursor,
297 message.into(),
298 )
299 }
300
301 pub(crate) fn store_invariant(message: impl Into<String>) -> Self {
303 Self::new(
304 ErrorClass::InvariantViolation,
305 ErrorOrigin::Store,
306 message.into(),
307 )
308 }
309
310 pub(crate) fn store_internal(message: impl Into<String>) -> Self {
312 Self::new(ErrorClass::Internal, ErrorOrigin::Store, message.into())
313 }
314
315 pub(crate) fn index_internal(message: impl Into<String>) -> Self {
317 Self::new(ErrorClass::Internal, ErrorOrigin::Index, message.into())
318 }
319
320 #[cfg(test)]
322 pub(crate) fn query_internal(message: impl Into<String>) -> Self {
323 Self::new(ErrorClass::Internal, ErrorOrigin::Query, message.into())
324 }
325
326 pub(crate) fn query_unsupported(message: impl Into<String>) -> Self {
328 Self::new(ErrorClass::Unsupported, ErrorOrigin::Query, message.into())
329 }
330
331 pub(crate) fn serialize_internal(message: impl Into<String>) -> Self {
333 Self::new(ErrorClass::Internal, ErrorOrigin::Serialize, message.into())
334 }
335
336 pub(crate) fn store_corruption(message: impl Into<String>) -> Self {
338 Self::new(ErrorClass::Corruption, ErrorOrigin::Store, message.into())
339 }
340
341 pub(crate) fn index_corruption(message: impl Into<String>) -> Self {
343 Self::new(ErrorClass::Corruption, ErrorOrigin::Index, message.into())
344 }
345
346 pub(crate) fn serialize_corruption(message: impl Into<String>) -> Self {
348 Self::new(
349 ErrorClass::Corruption,
350 ErrorOrigin::Serialize,
351 message.into(),
352 )
353 }
354
355 pub(crate) fn identity_corruption(message: impl Into<String>) -> Self {
357 Self::new(
358 ErrorClass::Corruption,
359 ErrorOrigin::Identity,
360 message.into(),
361 )
362 }
363
364 pub(crate) fn store_unsupported(message: impl Into<String>) -> Self {
366 Self::new(ErrorClass::Unsupported, ErrorOrigin::Store, message.into())
367 }
368
369 pub(crate) fn index_unsupported(message: impl Into<String>) -> Self {
371 Self::new(ErrorClass::Unsupported, ErrorOrigin::Index, message.into())
372 }
373
374 pub(crate) fn serialize_unsupported(message: impl Into<String>) -> Self {
376 Self::new(
377 ErrorClass::Unsupported,
378 ErrorOrigin::Serialize,
379 message.into(),
380 )
381 }
382
383 pub(crate) fn cursor_unsupported(message: impl Into<String>) -> Self {
385 Self::new(ErrorClass::Unsupported, ErrorOrigin::Cursor, message.into())
386 }
387
388 pub(crate) fn serialize_incompatible_persisted_format(message: impl Into<String>) -> Self {
390 Self::new(
391 ErrorClass::IncompatiblePersistedFormat,
392 ErrorOrigin::Serialize,
393 message.into(),
394 )
395 }
396
397 #[cfg(feature = "sql")]
400 pub(crate) fn query_unsupported_sql_feature(feature: &'static str) -> Self {
401 let message = format!(
402 "SQL query is not executable in this release: unsupported SQL feature: {feature}"
403 );
404
405 Self {
406 class: ErrorClass::Unsupported,
407 origin: ErrorOrigin::Query,
408 message,
409 detail: Some(ErrorDetail::Query(
410 QueryErrorDetail::UnsupportedSqlFeature { feature },
411 )),
412 }
413 }
414
415 pub fn store_not_found(key: impl Into<String>) -> Self {
416 let key = key.into();
417
418 Self {
419 class: ErrorClass::NotFound,
420 origin: ErrorOrigin::Store,
421 message: format!("data key not found: {key}"),
422 detail: Some(ErrorDetail::Store(StoreError::NotFound { key })),
423 }
424 }
425
426 pub fn unsupported_entity_path(path: impl Into<String>) -> Self {
428 let path = path.into();
429
430 Self::new(
431 ErrorClass::Unsupported,
432 ErrorOrigin::Store,
433 format!("unsupported entity path: '{path}'"),
434 )
435 }
436
437 #[must_use]
438 pub const fn is_not_found(&self) -> bool {
439 matches!(
440 self.detail,
441 Some(ErrorDetail::Store(StoreError::NotFound { .. }))
442 )
443 }
444
445 #[must_use]
446 pub fn display_with_class(&self) -> String {
447 format!("{}:{}: {}", self.origin, self.class, self.message)
448 }
449
450 pub(crate) fn index_plan_corruption(origin: ErrorOrigin, message: impl Into<String>) -> Self {
452 let message = message.into();
453 Self::new(
454 ErrorClass::Corruption,
455 origin,
456 format!("corruption detected ({origin}): {message}"),
457 )
458 }
459
460 pub(crate) fn index_plan_index_corruption(message: impl Into<String>) -> Self {
462 Self::index_plan_corruption(ErrorOrigin::Index, message)
463 }
464
465 pub(crate) fn index_plan_store_corruption(message: impl Into<String>) -> Self {
467 Self::index_plan_corruption(ErrorOrigin::Store, message)
468 }
469
470 pub(crate) fn index_plan_serialize_corruption(message: impl Into<String>) -> Self {
472 Self::index_plan_corruption(ErrorOrigin::Serialize, message)
473 }
474
475 pub(crate) fn index_plan_invariant(origin: ErrorOrigin, message: impl Into<String>) -> Self {
477 let message = message.into();
478 Self::new(
479 ErrorClass::InvariantViolation,
480 origin,
481 format!("invariant violation detected ({origin}): {message}"),
482 )
483 }
484
485 pub(crate) fn index_plan_store_invariant(message: impl Into<String>) -> Self {
487 Self::index_plan_invariant(ErrorOrigin::Store, message)
488 }
489
490 pub(crate) fn index_violation(path: &str, index_fields: &[&str]) -> Self {
492 Self::new(
493 ErrorClass::Conflict,
494 ErrorOrigin::Index,
495 format!(
496 "index constraint violation: {path} ({})",
497 index_fields.join(", ")
498 ),
499 )
500 }
501}
502
503#[derive(Debug, ThisError)]
511pub enum ErrorDetail {
512 #[error("{0}")]
513 Store(StoreError),
514 #[error("{0}")]
515 Query(QueryErrorDetail),
516 }
523
524#[derive(Debug, ThisError)]
532pub enum StoreError {
533 #[error("key not found: {key}")]
534 NotFound { key: String },
535
536 #[error("store corruption: {message}")]
537 Corrupt { message: String },
538
539 #[error("store invariant violation: {message}")]
540 InvariantViolation { message: String },
541}
542
543#[derive(Debug, ThisError)]
550pub enum QueryErrorDetail {
551 #[error("unsupported SQL feature: {feature}")]
552 UnsupportedSqlFeature { feature: &'static str },
553}
554
555#[derive(Clone, Copy, Debug, Eq, PartialEq)]
562pub enum ErrorClass {
563 Corruption,
564 IncompatiblePersistedFormat,
565 NotFound,
566 Internal,
567 Conflict,
568 Unsupported,
569 InvariantViolation,
570}
571
572impl fmt::Display for ErrorClass {
573 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574 let label = match self {
575 Self::Corruption => "corruption",
576 Self::IncompatiblePersistedFormat => "incompatible_persisted_format",
577 Self::NotFound => "not_found",
578 Self::Internal => "internal",
579 Self::Conflict => "conflict",
580 Self::Unsupported => "unsupported",
581 Self::InvariantViolation => "invariant_violation",
582 };
583 write!(f, "{label}")
584 }
585}
586
587#[derive(Clone, Copy, Debug, Eq, PartialEq)]
594pub enum ErrorOrigin {
595 Serialize,
596 Store,
597 Index,
598 Identity,
599 Query,
600 Planner,
601 Cursor,
602 Recovery,
603 Response,
604 Executor,
605 Interface,
606}
607
608impl fmt::Display for ErrorOrigin {
609 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610 let label = match self {
611 Self::Serialize => "serialize",
612 Self::Store => "store",
613 Self::Index => "index",
614 Self::Identity => "identity",
615 Self::Query => "query",
616 Self::Planner => "planner",
617 Self::Cursor => "cursor",
618 Self::Recovery => "recovery",
619 Self::Response => "response",
620 Self::Executor => "executor",
621 Self::Interface => "interface",
622 };
623 write!(f, "{label}")
624 }
625}