1#[cfg(test)]
8mod tests;
9
10use crate::patch::MergePatchError;
11use std::fmt;
12use thiserror::Error as ThisError;
13
14#[derive(Debug, ThisError)]
115#[error("{message}")]
116pub struct InternalError {
117 pub(crate) class: ErrorClass,
118 pub(crate) origin: ErrorOrigin,
119 pub(crate) message: String,
120
121 pub(crate) detail: Option<ErrorDetail>,
124}
125
126impl InternalError {
127 pub fn new(class: ErrorClass, origin: ErrorOrigin, message: impl Into<String>) -> Self {
131 let message = message.into();
132
133 let detail = match (class, origin) {
134 (ErrorClass::Corruption, ErrorOrigin::Store) => {
135 Some(ErrorDetail::Store(StoreError::Corrupt {
136 message: message.clone(),
137 }))
138 }
139 (ErrorClass::InvariantViolation, ErrorOrigin::Store) => {
140 Some(ErrorDetail::Store(StoreError::InvariantViolation {
141 message: message.clone(),
142 }))
143 }
144 _ => None,
145 };
146
147 Self {
148 class,
149 origin,
150 message,
151 detail,
152 }
153 }
154
155 #[must_use]
157 pub const fn class(&self) -> ErrorClass {
158 self.class
159 }
160
161 #[must_use]
163 pub const fn origin(&self) -> ErrorOrigin {
164 self.origin
165 }
166
167 #[must_use]
169 pub fn message(&self) -> &str {
170 &self.message
171 }
172
173 #[must_use]
175 pub const fn detail(&self) -> Option<&ErrorDetail> {
176 self.detail.as_ref()
177 }
178
179 #[must_use]
181 pub fn into_message(self) -> String {
182 self.message
183 }
184
185 pub(crate) fn classified(
187 class: ErrorClass,
188 origin: ErrorOrigin,
189 message: impl Into<String>,
190 ) -> Self {
191 Self::new(class, origin, message)
192 }
193
194 pub(crate) fn with_message(self, message: impl Into<String>) -> Self {
196 Self::classified(self.class, self.origin, message)
197 }
198
199 pub(crate) fn with_origin(self, origin: ErrorOrigin) -> Self {
203 Self::classified(self.class, origin, self.message)
204 }
205
206 pub(crate) fn index_invariant(message: impl Into<String>) -> Self {
208 Self::new(
209 ErrorClass::InvariantViolation,
210 ErrorOrigin::Index,
211 message.into(),
212 )
213 }
214
215 pub(crate) fn store_invariant(message: impl Into<String>) -> Self {
217 Self::new(
218 ErrorClass::InvariantViolation,
219 ErrorOrigin::Store,
220 message.into(),
221 )
222 }
223
224 pub(crate) fn store_internal(message: impl Into<String>) -> Self {
226 Self::new(ErrorClass::Internal, ErrorOrigin::Store, message.into())
227 }
228
229 pub(crate) fn index_internal(message: impl Into<String>) -> Self {
231 Self::new(ErrorClass::Internal, ErrorOrigin::Index, message.into())
232 }
233
234 #[cfg(test)]
236 pub(crate) fn query_internal(message: impl Into<String>) -> Self {
237 Self::new(ErrorClass::Internal, ErrorOrigin::Query, message.into())
238 }
239
240 pub(crate) fn serialize_internal(message: impl Into<String>) -> Self {
242 Self::new(ErrorClass::Internal, ErrorOrigin::Serialize, message.into())
243 }
244
245 pub(crate) fn store_corruption(message: impl Into<String>) -> Self {
247 Self::new(ErrorClass::Corruption, ErrorOrigin::Store, message.into())
248 }
249
250 pub(crate) fn index_corruption(message: impl Into<String>) -> Self {
252 Self::new(ErrorClass::Corruption, ErrorOrigin::Index, message.into())
253 }
254
255 pub(crate) fn serialize_corruption(message: impl Into<String>) -> Self {
257 Self::new(
258 ErrorClass::Corruption,
259 ErrorOrigin::Serialize,
260 message.into(),
261 )
262 }
263
264 pub(crate) fn identity_corruption(message: impl Into<String>) -> Self {
266 Self::new(
267 ErrorClass::Corruption,
268 ErrorOrigin::Identity,
269 message.into(),
270 )
271 }
272
273 pub(crate) fn store_unsupported(message: impl Into<String>) -> Self {
275 Self::new(ErrorClass::Unsupported, ErrorOrigin::Store, message.into())
276 }
277
278 pub(crate) fn index_unsupported(message: impl Into<String>) -> Self {
280 Self::new(ErrorClass::Unsupported, ErrorOrigin::Index, message.into())
281 }
282
283 pub(crate) fn serialize_unsupported(message: impl Into<String>) -> Self {
285 Self::new(
286 ErrorClass::Unsupported,
287 ErrorOrigin::Serialize,
288 message.into(),
289 )
290 }
291
292 pub(crate) fn cursor_unsupported(message: impl Into<String>) -> Self {
294 Self::new(ErrorClass::Unsupported, ErrorOrigin::Cursor, message.into())
295 }
296
297 pub(crate) fn serialize_incompatible_persisted_format(message: impl Into<String>) -> Self {
299 Self::new(
300 ErrorClass::IncompatiblePersistedFormat,
301 ErrorOrigin::Serialize,
302 message.into(),
303 )
304 }
305
306 #[cfg(feature = "sql")]
309 pub(crate) fn query_unsupported_sql_feature(feature: &'static str) -> Self {
310 let message = format!(
311 "SQL query is not executable in this release: unsupported SQL feature: {feature}"
312 );
313
314 Self {
315 class: ErrorClass::Unsupported,
316 origin: ErrorOrigin::Query,
317 message,
318 detail: Some(ErrorDetail::Query(
319 QueryErrorDetail::UnsupportedSqlFeature { feature },
320 )),
321 }
322 }
323
324 pub fn store_not_found(key: impl Into<String>) -> Self {
325 let key = key.into();
326
327 Self {
328 class: ErrorClass::NotFound,
329 origin: ErrorOrigin::Store,
330 message: format!("data key not found: {key}"),
331 detail: Some(ErrorDetail::Store(StoreError::NotFound { key })),
332 }
333 }
334
335 pub fn unsupported_entity_path(path: impl Into<String>) -> Self {
337 let path = path.into();
338
339 Self::new(
340 ErrorClass::Unsupported,
341 ErrorOrigin::Store,
342 format!("unsupported entity path: '{path}'"),
343 )
344 }
345
346 #[must_use]
347 pub const fn is_not_found(&self) -> bool {
348 matches!(
349 self.detail,
350 Some(ErrorDetail::Store(StoreError::NotFound { .. }))
351 )
352 }
353
354 #[must_use]
355 pub fn display_with_class(&self) -> String {
356 format!("{}:{}: {}", self.origin, self.class, self.message)
357 }
358
359 pub(crate) fn index_plan_corruption(origin: ErrorOrigin, message: impl Into<String>) -> Self {
361 let message = message.into();
362 Self::new(
363 ErrorClass::Corruption,
364 origin,
365 format!("corruption detected ({origin}): {message}"),
366 )
367 }
368
369 pub(crate) fn index_plan_index_corruption(message: impl Into<String>) -> Self {
371 Self::index_plan_corruption(ErrorOrigin::Index, message)
372 }
373
374 pub(crate) fn index_plan_store_corruption(message: impl Into<String>) -> Self {
376 Self::index_plan_corruption(ErrorOrigin::Store, message)
377 }
378
379 pub(crate) fn index_plan_serialize_corruption(message: impl Into<String>) -> Self {
381 Self::index_plan_corruption(ErrorOrigin::Serialize, message)
382 }
383
384 pub(crate) fn index_plan_invariant(origin: ErrorOrigin, message: impl Into<String>) -> Self {
386 let message = message.into();
387 Self::new(
388 ErrorClass::InvariantViolation,
389 origin,
390 format!("invariant violation detected ({origin}): {message}"),
391 )
392 }
393
394 pub(crate) fn index_plan_store_invariant(message: impl Into<String>) -> Self {
396 Self::index_plan_invariant(ErrorOrigin::Store, message)
397 }
398
399 pub(crate) fn index_violation(path: &str, index_fields: &[&str]) -> Self {
401 Self::new(
402 ErrorClass::Conflict,
403 ErrorOrigin::Index,
404 format!(
405 "index constraint violation: {path} ({})",
406 index_fields.join(", ")
407 ),
408 )
409 }
410}
411
412#[derive(Debug, ThisError)]
420pub enum ErrorDetail {
421 #[error("{0}")]
422 Store(StoreError),
423 #[error("{0}")]
424 ViewPatch(crate::patch::MergePatchError),
425 #[error("{0}")]
426 Query(QueryErrorDetail),
427 }
434
435impl From<MergePatchError> for InternalError {
436 fn from(err: MergePatchError) -> Self {
437 Self {
438 class: ErrorClass::Unsupported,
439 origin: ErrorOrigin::Interface,
440 message: err.to_string(),
441 detail: Some(ErrorDetail::ViewPatch(err)),
442 }
443 }
444}
445
446#[derive(Debug, ThisError)]
454pub enum StoreError {
455 #[error("key not found: {key}")]
456 NotFound { key: String },
457
458 #[error("store corruption: {message}")]
459 Corrupt { message: String },
460
461 #[error("store invariant violation: {message}")]
462 InvariantViolation { message: String },
463}
464
465#[derive(Debug, ThisError)]
472pub enum QueryErrorDetail {
473 #[error("unsupported SQL feature: {feature}")]
474 UnsupportedSqlFeature { feature: &'static str },
475}
476
477#[derive(Clone, Copy, Debug, Eq, PartialEq)]
484pub enum ErrorClass {
485 Corruption,
486 IncompatiblePersistedFormat,
487 NotFound,
488 Internal,
489 Conflict,
490 Unsupported,
491 InvariantViolation,
492}
493
494impl fmt::Display for ErrorClass {
495 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
496 let label = match self {
497 Self::Corruption => "corruption",
498 Self::IncompatiblePersistedFormat => "incompatible_persisted_format",
499 Self::NotFound => "not_found",
500 Self::Internal => "internal",
501 Self::Conflict => "conflict",
502 Self::Unsupported => "unsupported",
503 Self::InvariantViolation => "invariant_violation",
504 };
505 write!(f, "{label}")
506 }
507}
508
509#[derive(Clone, Copy, Debug, Eq, PartialEq)]
516pub enum ErrorOrigin {
517 Serialize,
518 Store,
519 Index,
520 Identity,
521 Query,
522 Planner,
523 Cursor,
524 Recovery,
525 Response,
526 Executor,
527 Interface,
528}
529
530impl fmt::Display for ErrorOrigin {
531 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532 let label = match self {
533 Self::Serialize => "serialize",
534 Self::Store => "store",
535 Self::Index => "index",
536 Self::Identity => "identity",
537 Self::Query => "query",
538 Self::Planner => "planner",
539 Self::Cursor => "cursor",
540 Self::Recovery => "recovery",
541 Self::Response => "response",
542 Self::Executor => "executor",
543 Self::Interface => "interface",
544 };
545 write!(f, "{label}")
546 }
547}