Skip to main content

icydb_core/error/
mod.rs

1//! Module: error
2//!
3//! Defines the canonical runtime error taxonomy for `icydb-core`.
4//! This module owns the shared error classes, origins, details, and
5//! constructor entry points used across storage, planning, execution, and
6//! serialization boundaries.
7
8#[cfg(test)]
9mod tests;
10
11use icydb_diagnostic_code as diagnostic_code;
12use std::fmt;
13
14pub(crate) const COMPACT_QUERY_DIAGNOSTIC_MESSAGE: &str = "query diagnostic";
15const COMPACT_RUNTIME_DIAGNOSTIC_MESSAGE: &str = "runtime diagnostic";
16const COMPACT_STORE_DIAGNOSTIC_MESSAGE: &str = "store diagnostic";
17const COMPACT_INDEX_DIAGNOSTIC_MESSAGE: &str = "index diagnostic";
18const COMPACT_SERIALIZE_DIAGNOSTIC_MESSAGE: &str = "serialize diagnostic";
19const COMPACT_IDENTITY_DIAGNOSTIC_MESSAGE: &str = "identity diagnostic";
20
21const fn compact_message_for(_class: ErrorClass, origin: ErrorOrigin) -> &'static str {
22    match origin {
23        ErrorOrigin::Serialize => COMPACT_SERIALIZE_DIAGNOSTIC_MESSAGE,
24        ErrorOrigin::Store => COMPACT_STORE_DIAGNOSTIC_MESSAGE,
25        ErrorOrigin::Index => COMPACT_INDEX_DIAGNOSTIC_MESSAGE,
26        ErrorOrigin::Identity => COMPACT_IDENTITY_DIAGNOSTIC_MESSAGE,
27        ErrorOrigin::Query | ErrorOrigin::Planner | ErrorOrigin::Response => {
28            COMPACT_QUERY_DIAGNOSTIC_MESSAGE
29        }
30        ErrorOrigin::Cursor
31        | ErrorOrigin::Recovery
32        | ErrorOrigin::Executor
33        | ErrorOrigin::Interface => COMPACT_RUNTIME_DIAGNOSTIC_MESSAGE,
34    }
35}
36
37// ============================================================================
38// INTERNAL ERROR TAXONOMY — ARCHITECTURAL CONTRACT
39// ============================================================================
40//
41// This file defines the canonical runtime error classification system for
42// icydb-core. It is the single source of truth for:
43//
44//   • ErrorClass   (semantic domain)
45//   • ErrorOrigin  (subsystem boundary)
46//   • Structured detail payloads
47//   • Canonical constructor entry points
48//
49// -----------------------------------------------------------------------------
50// DESIGN INTENT
51// -----------------------------------------------------------------------------
52//
53// 1. InternalError is a *taxonomy carrier*, not a formatting utility.
54//
55//    - ErrorClass represents semantic meaning (corruption, invariant_violation,
56//      unsupported, etc).
57//    - ErrorOrigin represents the subsystem boundary (store, index, query,
58//      executor, serialize, interface, etc).
59//    - The (class, origin) pair must remain stable and intentional.
60//
61// 2. Call sites MUST prefer canonical constructors.
62//
63//    Do NOT construct errors manually via:
64//        InternalError::new(class, origin)
65//    unless you are defining a new canonical helper here.
66//
67//    If a pattern appears more than once, centralize it here.
68//
69// 3. Constructors in this file must represent real architectural boundaries.
70//
71//    Add a new helper ONLY if it:
72//
73//      • Encodes a cross-cutting invariant,
74//      • Represents a subsystem boundary,
75//      • Or prevents taxonomy drift across call sites.
76//
77//    Do NOT add feature-specific helpers.
78//    Do NOT add one-off formatting helpers.
79//    Do NOT turn this file into a generic message factory.
80//
81// 4. ErrorDetail must align with ErrorOrigin.
82//
83//    If detail is present, it MUST correspond to the origin.
84//    Do not attach mismatched detail variants.
85//
86// 5. Plan-layer errors are NOT runtime failures.
87//
88//    PlanError and CursorPlanError must be translated into
89//    executor/query invariants via the canonical mapping functions.
90//    Do not leak plan-layer error types across execution boundaries.
91//
92// 6. Preserve taxonomy stability.
93//
94//    Do NOT:
95//      • Merge error classes.
96//      • Reclassify corruption as internal.
97//      • Downgrade invariant violations.
98//      • Introduce ambiguous class/origin combinations.
99//
100//    Any change to ErrorClass or ErrorOrigin is an architectural change
101//    and must be reviewed accordingly.
102//
103// -----------------------------------------------------------------------------
104// NON-GOALS
105// -----------------------------------------------------------------------------
106//
107// This is NOT:
108//
109//   • A public API contract.
110//   • A generic error abstraction layer.
111//   • A feature-specific message builder.
112//   • A dumping ground for temporary error conversions.
113//
114// -----------------------------------------------------------------------------
115// MAINTENANCE GUIDELINES
116// -----------------------------------------------------------------------------
117//
118// When modifying this file:
119//
120//   1. Ensure classification semantics remain consistent.
121//   2. Avoid constructor proliferation.
122//   3. Prefer narrow, origin-specific helpers over ad-hoc new(...).
123//   4. Keep formatting minimal and standardized.
124//   5. Keep this file boring and stable.
125//
126// If this file grows rapidly, something is wrong at the call sites.
127//
128// ============================================================================
129
130///
131/// InternalError
132///
133/// Structured runtime error with a stable internal classification.
134/// Not a stable API; intended for internal use and may change without notice.
135///
136
137pub struct InternalError {
138    pub(crate) class: ErrorClass,
139    pub(crate) origin: ErrorOrigin,
140
141    /// Optional structured error detail.
142    /// The variant (if present) must correspond to `origin`.
143    pub(crate) detail: Option<ErrorDetail>,
144}
145
146#[expect(
147    clippy::missing_const_for_fn,
148    reason = "internal error constructors stay non-const so compact diagnostic construction does not force const churn across subsystem helper seams"
149)]
150impl InternalError {
151    /// Construct an InternalError with optional origin-specific detail.
152    /// This constructor provides default StoreError details for certain
153    /// (class, origin) combinations but does not guarantee a detail payload.
154    #[must_use]
155    #[cold]
156    #[inline(never)]
157    pub fn new(class: ErrorClass, origin: ErrorOrigin) -> Self {
158        let detail = match (class, origin) {
159            (ErrorClass::Corruption, ErrorOrigin::Store) => {
160                Some(ErrorDetail::Store(StoreError::Corrupt))
161            }
162            (ErrorClass::InvariantViolation, ErrorOrigin::Store) => {
163                Some(ErrorDetail::Store(StoreError::InvariantViolation))
164            }
165            _ => None,
166        };
167
168        Self {
169            class,
170            origin,
171            detail,
172        }
173    }
174
175    /// Return the internal error class taxonomy.
176    #[must_use]
177    pub const fn class(&self) -> ErrorClass {
178        self.class
179    }
180
181    /// Return the internal error origin taxonomy.
182    #[must_use]
183    pub const fn origin(&self) -> ErrorOrigin {
184        self.origin
185    }
186
187    /// Return the rendered internal error message.
188    #[must_use]
189    pub const fn message(&self) -> &'static str {
190        compact_message_for(self.class, self.origin)
191    }
192
193    /// Return the optional structured detail payload.
194    #[must_use]
195    pub const fn detail(&self) -> Option<&ErrorDetail> {
196        self.detail.as_ref()
197    }
198
199    /// Return compact diagnostic identity for this internal error.
200    #[must_use]
201    pub fn diagnostic(&self) -> diagnostic_code::Diagnostic {
202        diagnostic_code::Diagnostic::new(
203            self.diagnostic_code(),
204            self.origin.diagnostic_origin(),
205            self.detail
206                .as_ref()
207                .and_then(ErrorDetail::diagnostic_detail),
208        )
209    }
210
211    /// Return the compact diagnostic code for this internal error.
212    #[must_use]
213    pub fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
214        self.detail.as_ref().map_or_else(
215            || self.class.diagnostic_code(self.origin),
216            ErrorDetail::diagnostic_code,
217        )
218    }
219
220    /// Consume and return the rendered internal error message.
221    #[must_use]
222    pub fn into_message(self) -> String {
223        self.message().to_string()
224    }
225
226    /// Construct an error while preserving an explicit class/origin taxonomy pair.
227    #[cold]
228    #[inline(never)]
229    pub(crate) fn classified(class: ErrorClass, origin: ErrorOrigin) -> Self {
230        Self::new(class, origin)
231    }
232
233    /// Rebuild this error with a new origin while preserving class taxonomy.
234    ///
235    /// Origin-scoped detail payloads are intentionally dropped when re-origining.
236    #[cold]
237    #[inline(never)]
238    pub(crate) fn with_origin(self, origin: ErrorOrigin) -> Self {
239        Self::classified(self.class, origin)
240    }
241
242    /// Construct an index-origin invariant violation.
243    #[cold]
244    #[inline(never)]
245    pub(crate) fn index_invariant() -> Self {
246        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Index)
247    }
248
249    /// Construct the canonical index field-count invariant for key building.
250    pub(crate) fn index_key_field_count_exceeds_max(
251        _index_name: &str,
252        _field_count: usize,
253        _max_fields: usize,
254    ) -> Self {
255        Self::index_invariant()
256    }
257
258    /// Construct the canonical index-key source-field-missing-on-model invariant.
259    pub(crate) fn index_key_item_field_missing_on_entity_model(_field: &str) -> Self {
260        Self::index_invariant()
261    }
262
263    /// Construct the canonical index-key source-field-missing-on-row invariant.
264    pub(crate) fn index_key_item_field_missing_on_lookup_row(_field: &str) -> Self {
265        Self::index_invariant()
266    }
267
268    /// Construct the canonical index-expression source-type mismatch invariant.
269    pub(crate) fn index_expression_source_type_mismatch(
270        _index_name: &str,
271        _expression: impl Sized,
272        _expected: impl Sized,
273        _source_label: &str,
274    ) -> Self {
275        Self::index_invariant()
276    }
277
278    /// Construct a planner-origin invariant violation for executor-boundary
279    /// contract drift.
280    #[cold]
281    #[inline(never)]
282    pub(crate) fn planner_executor_invariant() -> Self {
283        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Planner)
284    }
285
286    /// Construct a query-origin invariant violation for executor-boundary
287    /// contract drift.
288    #[cold]
289    #[inline(never)]
290    pub(crate) fn query_executor_invariant() -> Self {
291        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Query)
292    }
293
294    /// Construct a cursor-origin invariant violation for executor-boundary
295    /// contract drift.
296    #[cold]
297    #[inline(never)]
298    pub(crate) fn cursor_executor_invariant() -> Self {
299        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Cursor)
300    }
301
302    /// Construct an executor-origin invariant violation.
303    #[cold]
304    #[inline(never)]
305    pub(crate) fn executor_invariant() -> Self {
306        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Executor)
307    }
308
309    /// Construct an executor-origin internal error.
310    #[cold]
311    #[inline(never)]
312    pub(crate) fn executor_internal() -> Self {
313        Self::new(ErrorClass::Internal, ErrorOrigin::Executor)
314    }
315
316    /// Construct an executor-origin unsupported error.
317    #[cold]
318    #[inline(never)]
319    pub(crate) fn executor_unsupported() -> Self {
320        Self::new(ErrorClass::Unsupported, ErrorOrigin::Executor)
321    }
322
323    /// Construct an executor-origin save-preflight primary-key missing invariant.
324    pub(crate) fn mutation_entity_primary_key_missing(
325        _entity_path: &str,
326        _field_name: &str,
327    ) -> Self {
328        Self::executor_invariant()
329    }
330
331    /// Construct an executor-origin save-preflight primary-key invalid-value invariant.
332    pub(crate) fn mutation_entity_primary_key_invalid_value(
333        _entity_path: &str,
334        _field_name: &str,
335        _value: &crate::value::Value,
336    ) -> Self {
337        Self::executor_invariant()
338    }
339
340    /// Construct an executor-origin save-preflight primary-key type mismatch invariant.
341    pub(crate) fn mutation_entity_primary_key_type_mismatch(
342        _entity_path: &str,
343        _field_name: &str,
344        _value: &crate::value::Value,
345    ) -> Self {
346        Self::executor_invariant()
347    }
348
349    /// Construct an executor-origin save-preflight primary-key identity mismatch invariant.
350    pub(crate) fn mutation_entity_primary_key_mismatch(
351        _entity_path: &str,
352        _field_name: &str,
353        _field_value: &crate::value::Value,
354        _identity_key: &crate::value::Value,
355    ) -> Self {
356        Self::executor_invariant()
357    }
358
359    /// Construct an executor-origin save-preflight field-missing invariant.
360    pub(crate) fn mutation_entity_field_missing(
361        _entity_path: &str,
362        _field_name: &str,
363        _indexed: bool,
364    ) -> Self {
365        Self::executor_invariant()
366    }
367
368    /// Construct an executor-origin sparse structural patch required-field invariant.
369    pub(crate) fn mutation_structural_patch_required_field_missing(
370        _entity_path: &str,
371        _field_name: &str,
372    ) -> Self {
373        Self::executor_invariant()
374    }
375
376    /// Construct an executor-origin save-preflight field-type mismatch invariant.
377    pub(crate) fn mutation_entity_field_type_mismatch(
378        _entity_path: &str,
379        _field_name: &str,
380        _value: &crate::value::Value,
381    ) -> Self {
382        Self::executor_invariant()
383    }
384
385    /// Construct an executor-origin generated-field authored-write rejection.
386    pub(crate) fn mutation_generated_field_explicit(_entity_path: &str, _field_name: &str) -> Self {
387        Self::executor_unsupported()
388    }
389
390    /// Construct an executor-origin typed create omission rejection.
391    #[must_use]
392    pub fn mutation_create_missing_authored_fields(_entity_path: &str, _field_names: &str) -> Self {
393        Self::executor_unsupported()
394    }
395
396    /// Construct an executor-origin mutation result invariant.
397    ///
398    /// This constructor lands ahead of the public structural mutation surface,
399    /// so the library target may not route through it until that caller exists.
400    pub(crate) fn mutation_structural_after_image_invalid(
401        _entity_path: &str,
402        _data_key: impl Sized,
403        _detail: impl Sized,
404    ) -> Self {
405        Self::executor_invariant()
406    }
407
408    /// Construct an executor-origin mutation unknown-field invariant.
409    pub(crate) fn mutation_structural_field_unknown(_entity_path: &str, _field_name: &str) -> Self {
410        Self::executor_invariant()
411    }
412
413    /// Construct an executor-origin save-preflight decimal-scale unsupported error.
414    pub(crate) fn mutation_decimal_scale_mismatch(
415        _entity_path: &str,
416        _field_name: &str,
417        _expected_scale: impl Sized,
418        _actual_scale: impl Sized,
419    ) -> Self {
420        Self::executor_unsupported()
421    }
422
423    /// Construct an executor-origin save-preflight text-length unsupported error.
424    pub(crate) fn mutation_text_max_len_exceeded(
425        _entity_path: &str,
426        _field_name: &str,
427        _max_len: impl Sized,
428        _actual_len: impl Sized,
429    ) -> Self {
430        Self::executor_unsupported()
431    }
432
433    /// Construct an executor-origin save-preflight set-encoding invariant.
434    pub(crate) fn mutation_set_field_list_required(_entity_path: &str, _field_name: &str) -> Self {
435        Self::executor_invariant()
436    }
437
438    /// Construct an executor-origin save-preflight set-canonicality invariant.
439    pub(crate) fn mutation_set_field_not_canonical(_entity_path: &str, _field_name: &str) -> Self {
440        Self::executor_invariant()
441    }
442
443    /// Construct an executor-origin save-preflight map-encoding invariant.
444    pub(crate) fn mutation_map_field_map_required(_entity_path: &str, _field_name: &str) -> Self {
445        Self::executor_invariant()
446    }
447
448    /// Construct an executor-origin save-preflight map-entry invariant.
449    pub(crate) fn mutation_map_field_entries_invalid(
450        _entity_path: &str,
451        _field_name: &str,
452        _detail: impl Sized,
453    ) -> Self {
454        Self::executor_invariant()
455    }
456
457    /// Construct an executor-origin save-preflight map-canonicality invariant.
458    pub(crate) fn mutation_map_field_entries_not_canonical(
459        _entity_path: &str,
460        _field_name: &str,
461    ) -> Self {
462        Self::executor_invariant()
463    }
464
465    /// Construct a query-origin scalar page invariant for ordering before filtering.
466    pub(crate) fn scalar_page_ordering_after_filtering_required() -> Self {
467        Self::query_executor_invariant()
468    }
469
470    /// Construct a query-origin scalar page invariant for missing order at the cursor boundary.
471    pub(crate) fn scalar_page_cursor_boundary_order_required() -> Self {
472        Self::query_executor_invariant()
473    }
474
475    /// Construct a query-origin scalar page invariant for cursor-before-ordering drift.
476    pub(crate) fn scalar_page_cursor_boundary_after_ordering_required() -> Self {
477        Self::query_executor_invariant()
478    }
479
480    /// Construct a query-origin scalar page invariant for pagination-before-ordering drift.
481    pub(crate) fn scalar_page_pagination_after_ordering_required() -> Self {
482        Self::query_executor_invariant()
483    }
484
485    /// Construct a query-origin scalar page invariant for delete-limit-before-ordering drift.
486    pub(crate) fn scalar_page_delete_limit_after_ordering_required() -> Self {
487        Self::query_executor_invariant()
488    }
489
490    /// Construct a query-origin load-runtime invariant for scalar-mode payload mismatch.
491    pub(crate) fn load_runtime_scalar_payload_required() -> Self {
492        Self::query_executor_invariant()
493    }
494
495    /// Construct a query-origin load-runtime invariant for grouped-mode payload mismatch.
496    pub(crate) fn load_runtime_grouped_payload_required() -> Self {
497        Self::query_executor_invariant()
498    }
499
500    /// Construct a query-origin load-surface invariant for scalar-page payload mismatch.
501    pub(crate) fn load_runtime_scalar_surface_payload_required() -> Self {
502        Self::query_executor_invariant()
503    }
504
505    /// Construct a query-origin load-surface invariant for grouped-page payload mismatch.
506    pub(crate) fn load_runtime_grouped_surface_payload_required() -> Self {
507        Self::query_executor_invariant()
508    }
509
510    /// Construct a query-origin load-entrypoint invariant for non-load plans.
511    pub(crate) fn load_executor_load_plan_required() -> Self {
512        Self::query_executor_invariant()
513    }
514
515    /// Construct an executor-origin delete-entrypoint unsupported grouped-mode error.
516    pub(crate) fn delete_executor_grouped_unsupported() -> Self {
517        Self::executor_unsupported()
518    }
519
520    /// Construct a query-origin delete-entrypoint invariant for non-delete plans.
521    pub(crate) fn delete_executor_delete_plan_required() -> Self {
522        Self::query_executor_invariant()
523    }
524
525    /// Construct a query-origin aggregate kernel invariant for fold-mode contract drift.
526    pub(crate) fn aggregate_fold_mode_terminal_contract_required() -> Self {
527        Self::query_executor_invariant()
528    }
529
530    /// Construct a query-origin fast-stream invariant for route kind/request mismatch.
531    pub(crate) fn fast_stream_route_kind_request_match_required() -> Self {
532        Self::query_executor_invariant()
533    }
534
535    /// Construct a query-origin scan invariant for missing index-prefix executable specs.
536    pub(crate) fn secondary_index_prefix_spec_required() -> Self {
537        Self::query_executor_invariant()
538    }
539
540    /// Construct a query-origin scan invariant for missing index-range executable specs.
541    pub(crate) fn index_range_limit_spec_required() -> Self {
542        Self::query_executor_invariant()
543    }
544
545    /// Construct an executor-origin mutation unsupported error for duplicate atomic save keys.
546    pub(crate) fn mutation_atomic_save_duplicate_key(_entity_path: &str, _key: impl Sized) -> Self {
547        Self::executor_unsupported()
548    }
549
550    /// Construct an executor-origin mutation invariant for index-store generation drift.
551    pub(crate) fn mutation_index_store_generation_changed(
552        _expected_generation: u64,
553        _observed_generation: u64,
554    ) -> Self {
555        Self::executor_invariant()
556    }
557
558    /// Construct a planner-origin invariant violation.
559    #[cold]
560    #[inline(never)]
561    pub(crate) fn planner_invariant() -> Self {
562        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Planner)
563    }
564
565    /// Construct a planner-origin invalid-logical-plan invariant.
566    pub(crate) fn query_invalid_logical_plan() -> Self {
567        Self::planner_invariant()
568    }
569
570    /// Construct a store-origin invariant violation.
571    pub(crate) fn store_invariant() -> Self {
572        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Store)
573    }
574
575    /// Construct the canonical duplicate runtime-hook entity-tag invariant.
576    pub(crate) fn duplicate_runtime_hooks_for_entity_tag(
577        _entity_tag: crate::types::EntityTag,
578    ) -> Self {
579        Self::store_invariant()
580    }
581
582    /// Construct the canonical duplicate runtime-hook entity-path invariant.
583    pub(crate) fn duplicate_runtime_hooks_for_entity_path(_entity_path: &str) -> Self {
584        Self::store_invariant()
585    }
586
587    /// Construct a store-origin internal error.
588    #[cold]
589    #[inline(never)]
590    pub(crate) fn store_internal() -> Self {
591        Self::new(ErrorClass::Internal, ErrorOrigin::Store)
592    }
593
594    /// Construct the canonical unconfigured commit-memory id internal error.
595    pub(crate) fn commit_memory_id_unconfigured() -> Self {
596        Self::store_internal()
597    }
598
599    /// Construct the canonical commit-memory id mismatch internal error.
600    pub(crate) fn commit_memory_id_mismatch(_cached_id: u8, _configured_id: u8) -> Self {
601        Self::store_internal()
602    }
603
604    /// Construct the canonical commit-memory stable-key mismatch internal error.
605    pub(crate) fn commit_memory_stable_key_mismatch(
606        _cached_key: &str,
607        _configured_key: &str,
608    ) -> Self {
609        Self::store_internal()
610    }
611
612    /// Construct the canonical missing rollback-row invariant for delete execution.
613    pub(crate) fn delete_rollback_row_required() -> Self {
614        Self::store_internal()
615    }
616
617    /// Construct the canonical recovery-integrity totals corruption error.
618    pub(crate) fn recovery_integrity_validation_failed(
619        _missing_index_entries: u64,
620        _divergent_index_entries: u64,
621        _orphan_index_references: u64,
622    ) -> Self {
623        Self::store_corruption()
624    }
625
626    /// Construct an index-origin internal error.
627    #[cold]
628    #[inline(never)]
629    pub(crate) fn index_internal() -> Self {
630        Self::new(ErrorClass::Internal, ErrorOrigin::Index)
631    }
632
633    /// Construct the canonical missing old entity-key internal error for structural index removal.
634    pub(crate) fn structural_index_removal_entity_key_required() -> Self {
635        Self::index_internal()
636    }
637
638    /// Construct the canonical missing new entity-key internal error for structural index insertion.
639    pub(crate) fn structural_index_insertion_entity_key_required() -> Self {
640        Self::index_internal()
641    }
642
643    /// Construct the canonical missing old entity-key internal error for index commit-op removal.
644    pub(crate) fn index_commit_op_old_entity_key_required() -> Self {
645        Self::index_internal()
646    }
647
648    /// Construct the canonical missing new entity-key internal error for index commit-op insertion.
649    pub(crate) fn index_commit_op_new_entity_key_required() -> Self {
650        Self::index_internal()
651    }
652
653    /// Construct a query-origin internal error.
654    #[cfg(test)]
655    pub(crate) fn query_internal() -> Self {
656        Self::new(ErrorClass::Internal, ErrorOrigin::Query)
657    }
658
659    /// Construct a query-origin unsupported error.
660    #[cold]
661    #[inline(never)]
662    pub(crate) fn query_unsupported() -> Self {
663        Self::new(ErrorClass::Unsupported, ErrorOrigin::Query)
664    }
665
666    /// Construct a query-origin SQL DDL admission error with structured detail.
667    #[cold]
668    #[inline(never)]
669    #[cfg(feature = "sql")]
670    pub(crate) fn query_schema_ddl_admission(error: SchemaDdlAdmissionError) -> Self {
671        Self {
672            class: ErrorClass::Unsupported,
673            origin: ErrorOrigin::Query,
674            detail: Some(ErrorDetail::Query(QueryErrorDetail::SchemaDdlAdmission {
675                error,
676            })),
677        }
678    }
679
680    /// Construct a query-origin numeric overflow error with structured detail.
681    #[cold]
682    #[inline(never)]
683    pub(crate) fn query_numeric_overflow() -> Self {
684        Self {
685            class: ErrorClass::Unsupported,
686            origin: ErrorOrigin::Query,
687            detail: Some(ErrorDetail::Query(QueryErrorDetail::NumericOverflow)),
688        }
689    }
690
691    /// Construct a query-origin non-representable numeric result error with
692    /// structured detail.
693    #[cold]
694    #[inline(never)]
695    pub(crate) fn query_numeric_not_representable() -> Self {
696        Self {
697            class: ErrorClass::Unsupported,
698            origin: ErrorOrigin::Query,
699            detail: Some(ErrorDetail::Query(
700                QueryErrorDetail::NumericNotRepresentable,
701            )),
702        }
703    }
704
705    /// Construct a serialize-origin internal error.
706    #[cold]
707    #[inline(never)]
708    pub(crate) fn serialize_internal() -> Self {
709        Self::new(ErrorClass::Internal, ErrorOrigin::Serialize)
710    }
711
712    /// Construct the canonical persisted-row encode internal error.
713    pub(crate) fn persisted_row_encode_failed(_detail: impl Sized) -> Self {
714        Self::persisted_row_encode_internal()
715    }
716
717    /// Construct the compact persisted-row encode internal error.
718    pub(crate) fn persisted_row_encode_internal() -> Self {
719        Self::serialize_internal()
720    }
721
722    /// Construct the canonical persisted-row field encode internal error.
723    pub(crate) fn persisted_row_field_encode_failed(field_name: &str, _detail: impl Sized) -> Self {
724        Self::persisted_row_field_encode_internal(field_name)
725    }
726
727    /// Construct the compact persisted-row field encode internal error.
728    pub(crate) fn persisted_row_field_encode_internal(_field_name: &str) -> Self {
729        Self::persisted_row_encode_internal()
730    }
731
732    /// Construct the canonical bytes(field) value encode internal error.
733    pub(crate) fn bytes_field_value_encode_failed(_detail: impl Sized) -> Self {
734        Self::serialize_internal()
735    }
736
737    /// Construct a store-origin corruption error.
738    #[cold]
739    #[inline(never)]
740    pub(crate) fn store_corruption() -> Self {
741        Self::new(ErrorClass::Corruption, ErrorOrigin::Store)
742    }
743
744    /// Construct a store-origin commit-marker corruption error.
745    pub(crate) fn commit_corruption() -> Self {
746        Self::store_corruption()
747    }
748
749    /// Construct a store-origin commit-marker component corruption error.
750    pub(crate) fn commit_component_corruption() -> Self {
751        Self::commit_corruption()
752    }
753
754    /// Construct the canonical commit-marker id generation internal error.
755    pub(crate) fn commit_id_generation_failed() -> Self {
756        Self::store_internal()
757    }
758
759    /// Construct the canonical commit-marker payload u32-length-limit error.
760    pub(crate) fn commit_marker_payload_exceeds_u32_length_limit() -> Self {
761        Self::store_unsupported()
762    }
763
764    /// Construct the canonical commit-marker component invalid-length corruption error.
765    pub(crate) fn commit_component_length_invalid() -> Self {
766        Self::commit_corruption()
767    }
768
769    /// Construct the canonical commit-marker max-size corruption error.
770    pub(crate) fn commit_marker_exceeds_max_size() -> Self {
771        Self::commit_corruption()
772    }
773
774    /// Construct the canonical commit-control slot max-size unsupported error.
775    pub(crate) fn commit_control_slot_exceeds_max_size() -> Self {
776        Self::store_unsupported()
777    }
778
779    /// Construct the canonical commit-control marker-bytes length-limit error.
780    pub(crate) fn commit_control_slot_marker_bytes_exceed_u32_length_limit() -> Self {
781        Self::store_unsupported()
782    }
783
784    /// Construct the canonical startup index-rebuild invalid-data-key corruption error.
785    pub(crate) fn startup_index_rebuild_invalid_data_key() -> Self {
786        Self::store_corruption()
787    }
788
789    /// Construct an index-origin corruption error.
790    #[cold]
791    #[inline(never)]
792    pub(crate) fn index_corruption() -> Self {
793        Self::new(ErrorClass::Corruption, ErrorOrigin::Index)
794    }
795
796    /// Construct the canonical unique-validation corruption wrapper.
797    pub(crate) fn index_unique_validation_corruption() -> Self {
798        Self::index_plan_index_corruption()
799    }
800
801    /// Construct the canonical structural index-entry corruption wrapper.
802    pub(crate) fn structural_index_entry_corruption() -> Self {
803        Self::index_plan_index_corruption()
804    }
805
806    /// Construct the canonical missing new entity-key invariant during unique validation.
807    pub(crate) fn index_unique_validation_entity_key_required() -> Self {
808        Self::index_invariant()
809    }
810
811    /// Construct the canonical unique-validation structural row-decode corruption error.
812    pub(crate) fn index_unique_validation_row_deserialize_failed() -> Self {
813        Self::index_plan_serialize_corruption()
814    }
815
816    /// Construct the canonical unique-validation primary-key slot decode corruption error.
817    pub(crate) fn index_unique_validation_primary_key_decode_failed() -> Self {
818        Self::index_plan_serialize_corruption()
819    }
820
821    /// Construct the canonical unique-validation stored key rebuild corruption error.
822    pub(crate) fn index_unique_validation_key_rebuild_failed() -> Self {
823        Self::index_plan_serialize_corruption()
824    }
825
826    /// Construct the canonical unique-validation missing-row corruption error.
827    pub(crate) fn index_unique_validation_row_required() -> Self {
828        Self::index_plan_store_corruption()
829    }
830
831    /// Construct the canonical index-only predicate missing-component invariant.
832    pub(crate) fn index_only_predicate_component_required() -> Self {
833        Self::index_invariant()
834    }
835
836    /// Construct the canonical index-scan continuation-envelope invariant.
837    pub(crate) fn index_scan_continuation_anchor_within_envelope_required() -> Self {
838        Self::index_invariant()
839    }
840
841    /// Construct the canonical index-scan continuation-advancement invariant.
842    pub(crate) fn index_scan_continuation_advancement_required() -> Self {
843        Self::index_invariant()
844    }
845
846    /// Construct the canonical index-scan key-decode corruption error.
847    pub(crate) fn index_scan_key_corrupted_during(
848        _context: &'static str,
849        _err: impl Sized,
850    ) -> Self {
851        Self::index_corruption()
852    }
853
854    /// Construct the canonical index-scan missing projection-component invariant.
855    pub(crate) fn index_projection_component_required(
856        _index_name: &str,
857        _component_index: usize,
858    ) -> Self {
859        Self::index_invariant()
860    }
861
862    /// Construct the canonical scan-time index-entry decode corruption error.
863    pub(crate) fn index_entry_decode_failed() -> Self {
864        Self::index_corruption()
865    }
866
867    /// Construct a serialize-origin corruption error.
868    pub(crate) fn serialize_corruption() -> Self {
869        Self::new(ErrorClass::Corruption, ErrorOrigin::Serialize)
870    }
871
872    /// Construct the canonical persisted-row decode corruption error.
873    pub(crate) fn persisted_row_decode_failed(_detail: impl Sized) -> Self {
874        Self::persisted_row_decode_corruption()
875    }
876
877    /// Construct the compact persisted-row decode corruption error.
878    pub(crate) fn persisted_row_decode_corruption() -> Self {
879        Self::serialize_corruption()
880    }
881
882    /// Construct the canonical persisted-row field decode corruption error.
883    pub(crate) fn persisted_row_field_decode_failed(field_name: &str, _detail: impl Sized) -> Self {
884        Self::persisted_row_field_decode_corruption(field_name)
885    }
886
887    /// Construct the compact persisted-row field decode corruption error.
888    pub(crate) fn persisted_row_field_decode_corruption(_field_name: &str) -> Self {
889        Self::persisted_row_decode_corruption()
890    }
891
892    /// Construct the canonical persisted-row field-kind decode corruption error.
893    pub(crate) fn persisted_row_field_kind_decode_failed(
894        field_name: &str,
895        _field_kind: impl fmt::Debug,
896        _detail: impl Sized,
897    ) -> Self {
898        Self::persisted_row_field_decode_corruption(field_name)
899    }
900
901    /// Construct the canonical persisted-row scalar-payload length corruption error.
902    pub(crate) fn persisted_row_field_payload_exact_len_required(field_name: &str) -> Self {
903        Self::persisted_row_field_decode_corruption(field_name)
904    }
905
906    /// Construct the canonical persisted-row scalar-payload empty-body corruption error.
907    pub(crate) fn persisted_row_field_payload_must_be_empty(field_name: &str) -> Self {
908        Self::persisted_row_field_decode_corruption(field_name)
909    }
910
911    /// Construct the canonical persisted-row scalar-payload invalid-byte corruption error.
912    pub(crate) fn persisted_row_field_payload_invalid_byte(field_name: &str) -> Self {
913        Self::persisted_row_field_decode_corruption(field_name)
914    }
915
916    /// Construct the canonical persisted-row scalar-payload non-finite corruption error.
917    pub(crate) fn persisted_row_field_payload_non_finite(field_name: &str) -> Self {
918        Self::persisted_row_field_decode_corruption(field_name)
919    }
920
921    /// Construct the canonical persisted-row scalar-payload out-of-range corruption error.
922    pub(crate) fn persisted_row_field_payload_out_of_range(field_name: &str) -> Self {
923        Self::persisted_row_field_decode_corruption(field_name)
924    }
925
926    /// Construct the canonical persisted-row invalid text payload corruption error.
927    pub(crate) fn persisted_row_field_text_payload_invalid_utf8(field_name: &str) -> Self {
928        Self::persisted_row_field_decode_corruption(field_name)
929    }
930
931    /// Construct the canonical persisted-row structural slot-lookup invariant.
932    pub(crate) fn persisted_row_slot_lookup_out_of_bounds(_model_path: &str, _slot: usize) -> Self {
933        Self::index_invariant()
934    }
935
936    /// Construct the canonical persisted-row structural slot-cache invariant.
937    pub(crate) fn persisted_row_slot_cache_lookup_out_of_bounds(
938        _model_path: &str,
939        _slot: usize,
940    ) -> Self {
941        Self::index_invariant()
942    }
943
944    /// Construct the canonical persisted-row primary-key decode corruption error.
945    pub(crate) fn persisted_row_primary_key_not_primary_key_encodable(
946        _data_key: impl fmt::Debug,
947        _detail: impl Sized,
948    ) -> Self {
949        Self::persisted_row_decode_corruption()
950    }
951
952    /// Construct the canonical persisted-row missing primary-key slot corruption error.
953    pub(crate) fn persisted_row_primary_key_slot_missing(_data_key: impl fmt::Debug) -> Self {
954        Self::persisted_row_decode_corruption()
955    }
956
957    /// Construct the canonical persisted-row key mismatch corruption error.
958    pub(crate) fn persisted_row_key_mismatch() -> Self {
959        Self::store_corruption()
960    }
961
962    /// Construct the canonical persisted-row missing declared-field corruption error.
963    pub(crate) fn persisted_row_declared_field_missing(field_name: &str) -> Self {
964        Self::persisted_row_field_decode_corruption(field_name)
965    }
966
967    /// Construct the canonical data-key entity mismatch corruption error.
968    pub(crate) fn data_key_entity_mismatch() -> Self {
969        Self::store_corruption()
970    }
971
972    /// Construct the canonical reverse-index ordinal overflow internal error.
973    pub(crate) fn reverse_index_ordinal_overflow(
974        _source_path: &str,
975        _field_name: &str,
976        _target_path: &str,
977        _detail: impl Sized,
978    ) -> Self {
979        Self::index_internal()
980    }
981
982    /// Construct the canonical reverse-index entry corruption error.
983    pub(crate) fn reverse_index_entry_corrupted(
984        _source_path: &str,
985        _field_name: &str,
986        _target_path: &str,
987        _index_key: impl fmt::Debug,
988        _detail: impl Sized,
989    ) -> Self {
990        Self::index_corruption()
991    }
992
993    /// Construct the canonical relation-target store missing internal error.
994    pub(crate) fn relation_target_store_missing(
995        _source_path: &str,
996        _field_name: &str,
997        _target_path: &str,
998        _store_path: &str,
999        _detail: impl Sized,
1000    ) -> Self {
1001        Self::executor_internal()
1002    }
1003
1004    /// Construct the canonical relation-target key decode corruption error.
1005    pub(crate) fn relation_target_key_decode_failed(
1006        _context_label: &str,
1007        _source_path: &str,
1008        _field_name: &str,
1009        _target_path: &str,
1010        _detail: impl Sized,
1011    ) -> Self {
1012        Self::identity_corruption()
1013    }
1014
1015    /// Construct the canonical relation-target entity mismatch corruption error.
1016    pub(crate) fn relation_target_entity_mismatch(
1017        _context_label: &str,
1018        _source_path: &str,
1019        _field_name: &str,
1020        _target_path: &str,
1021        _target_entity_name: &str,
1022        _expected_tag: impl Sized,
1023        _actual_tag: impl Sized,
1024    ) -> Self {
1025        Self::store_corruption()
1026    }
1027
1028    /// Construct the canonical relation-source row decode corruption error.
1029    pub(crate) fn relation_source_row_decode_failed(
1030        _source_path: &str,
1031        _field_name: &str,
1032        _target_path: &str,
1033        _detail: impl Sized,
1034    ) -> Self {
1035        Self::persisted_row_decode_corruption()
1036    }
1037
1038    /// Construct the canonical relation-source unsupported scalar relation-key corruption error.
1039    pub(crate) fn relation_source_row_unsupported_scalar_relation_key(
1040        _source_path: &str,
1041        _field_name: &str,
1042        _target_path: &str,
1043    ) -> Self {
1044        Self::persisted_row_decode_corruption()
1045    }
1046
1047    /// Construct the canonical unsupported strong-relation key-kind corruption error.
1048    pub(crate) fn relation_source_row_unsupported_key_kind(_field_kind: impl fmt::Debug) -> Self {
1049        Self::persisted_row_decode_corruption()
1050    }
1051
1052    /// Construct the canonical reverse-index relation-target decode invariant failure.
1053    pub(crate) fn reverse_index_relation_target_decode_invariant_violated(
1054        _source_path: &str,
1055        _field_name: &str,
1056        _target_path: &str,
1057    ) -> Self {
1058        Self::executor_internal()
1059    }
1060
1061    /// Construct the canonical covering-component empty-payload corruption error.
1062    pub(crate) fn bytes_covering_component_payload_empty() -> Self {
1063        Self::index_corruption()
1064    }
1065
1066    /// Construct the canonical covering-component truncated bool corruption error.
1067    pub(crate) fn bytes_covering_bool_payload_truncated() -> Self {
1068        Self::index_corruption()
1069    }
1070
1071    /// Construct the canonical covering-component invalid-length corruption error.
1072    pub(crate) fn bytes_covering_component_payload_invalid_length() -> Self {
1073        Self::index_corruption()
1074    }
1075
1076    /// Construct the canonical covering-component invalid-bool corruption error.
1077    pub(crate) fn bytes_covering_bool_payload_invalid_value() -> Self {
1078        Self::index_corruption()
1079    }
1080
1081    /// Construct the canonical covering-component invalid text terminator corruption error.
1082    pub(crate) fn bytes_covering_text_payload_invalid_terminator() -> Self {
1083        Self::index_corruption()
1084    }
1085
1086    /// Construct the canonical covering-component trailing-text corruption error.
1087    pub(crate) fn bytes_covering_text_payload_trailing_bytes() -> Self {
1088        Self::index_corruption()
1089    }
1090
1091    /// Construct the canonical covering-component invalid-UTF-8 text corruption error.
1092    pub(crate) fn bytes_covering_text_payload_invalid_utf8() -> Self {
1093        Self::index_corruption()
1094    }
1095
1096    /// Construct the canonical covering-component invalid text escape corruption error.
1097    pub(crate) fn bytes_covering_text_payload_invalid_escape_byte() -> Self {
1098        Self::index_corruption()
1099    }
1100
1101    /// Construct the canonical covering-component missing text terminator corruption error.
1102    pub(crate) fn bytes_covering_text_payload_missing_terminator() -> Self {
1103        Self::index_corruption()
1104    }
1105
1106    /// Construct the canonical missing persisted-field decode error.
1107    #[must_use]
1108    pub fn missing_persisted_slot(field_name: &'static str) -> Self {
1109        Self::persisted_row_field_decode_corruption(field_name)
1110    }
1111
1112    /// Construct an identity-origin corruption error.
1113    pub(crate) fn identity_corruption() -> Self {
1114        Self::new(ErrorClass::Corruption, ErrorOrigin::Identity)
1115    }
1116
1117    /// Construct a store-origin unsupported error.
1118    #[cold]
1119    #[inline(never)]
1120    pub(crate) fn store_unsupported() -> Self {
1121        Self::new(ErrorClass::Unsupported, ErrorOrigin::Store)
1122    }
1123
1124    /// Construct the canonical schema DDL publication race error.
1125    pub(crate) fn schema_ddl_publication_race_lost(_entity_path: &'static str) -> Self {
1126        Self {
1127            class: ErrorClass::Unsupported,
1128            origin: ErrorOrigin::Store,
1129            detail: Some(ErrorDetail::Store(StoreError::SchemaDdlPublicationRaceLost)),
1130        }
1131    }
1132
1133    /// Construct the canonical SQL DDL SET NOT NULL validation failure.
1134    #[cfg(feature = "sql")]
1135    pub(crate) fn schema_ddl_set_not_null_validation_failed(
1136        _entity_path: &'static str,
1137        _column_name: &str,
1138    ) -> Self {
1139        Self {
1140            class: ErrorClass::Unsupported,
1141            origin: ErrorOrigin::Store,
1142            detail: Some(ErrorDetail::Store(
1143                StoreError::SchemaDdlSetNotNullValidationFailed,
1144            )),
1145        }
1146    }
1147
1148    /// Construct the canonical unsupported persisted entity-tag store error.
1149    pub(crate) fn unsupported_entity_tag_in_data_store(
1150        _entity_tag: crate::types::EntityTag,
1151    ) -> Self {
1152        Self::store_unsupported()
1153    }
1154
1155    /// Construct the canonical commit-memory id registration failure.
1156    #[cfg_attr(test, expect(dead_code))]
1157    pub(crate) fn commit_memory_id_registration_failed(_err: impl Sized) -> Self {
1158        Self::store_internal()
1159    }
1160
1161    /// Construct an index-origin unsupported error.
1162    pub(crate) fn index_unsupported() -> Self {
1163        Self::new(ErrorClass::Unsupported, ErrorOrigin::Index)
1164    }
1165
1166    /// Construct the canonical index-key component size-limit unsupported error.
1167    pub(crate) fn index_component_exceeds_max_size() -> Self {
1168        Self::index_unsupported()
1169    }
1170
1171    /// Construct a serialize-origin unsupported error.
1172    pub(crate) fn serialize_unsupported() -> Self {
1173        Self::new(ErrorClass::Unsupported, ErrorOrigin::Serialize)
1174    }
1175
1176    /// Construct a cursor-origin unsupported error.
1177    pub(crate) fn cursor_unsupported() -> Self {
1178        Self::new(ErrorClass::Unsupported, ErrorOrigin::Cursor)
1179    }
1180
1181    /// Construct a serialize-origin incompatible persisted-format error.
1182    pub(crate) fn serialize_incompatible_persisted_format() -> Self {
1183        Self::new(
1184            ErrorClass::IncompatiblePersistedFormat,
1185            ErrorOrigin::Serialize,
1186        )
1187    }
1188
1189    /// Construct a query-origin unsupported error preserving one SQL parser
1190    /// unsupported-feature code in structured error detail.
1191    #[cfg(feature = "sql")]
1192    pub(crate) fn query_unsupported_sql_feature(feature: diagnostic_code::SqlFeatureCode) -> Self {
1193        Self {
1194            class: ErrorClass::Unsupported,
1195            origin: ErrorOrigin::Query,
1196            detail: Some(ErrorDetail::Query(
1197                QueryErrorDetail::UnsupportedSqlFeature { feature },
1198            )),
1199        }
1200    }
1201
1202    /// Construct a query-origin unsupported SQL lowering error preserving one
1203    /// compact lowering reason in structured error detail.
1204    #[cfg(feature = "sql")]
1205    pub(crate) fn query_sql_lowering(reason: diagnostic_code::SqlLoweringCode) -> Self {
1206        Self {
1207            class: ErrorClass::Unsupported,
1208            origin: ErrorOrigin::Query,
1209            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlLowering { reason })),
1210        }
1211    }
1212
1213    /// Construct a query-origin unsupported projection error preserving one
1214    /// compact projection reason in structured error detail.
1215    pub(crate) fn query_unsupported_projection(
1216        reason: diagnostic_code::QueryProjectionCode,
1217    ) -> Self {
1218        Self {
1219            class: ErrorClass::Unsupported,
1220            origin: ErrorOrigin::Query,
1221            detail: Some(ErrorDetail::Query(
1222                QueryErrorDetail::UnsupportedProjection { reason },
1223            )),
1224        }
1225    }
1226
1227    /// Construct a query-origin unsupported aggregate target-field error.
1228    pub(crate) fn query_unknown_aggregate_target_field() -> Self {
1229        Self {
1230            class: ErrorClass::Unsupported,
1231            origin: ErrorOrigin::Query,
1232            detail: Some(ErrorDetail::Query(
1233                QueryErrorDetail::UnknownAggregateTargetField,
1234            )),
1235        }
1236    }
1237
1238    /// Construct a query-origin result-shape mismatch error preserving one
1239    /// compact result-shape reason in structured error detail.
1240    pub(crate) fn query_result_shape_mismatch(
1241        reason: diagnostic_code::QueryResultShapeCode,
1242    ) -> Self {
1243        Self {
1244            class: ErrorClass::Unsupported,
1245            origin: ErrorOrigin::Query,
1246            detail: Some(ErrorDetail::Query(QueryErrorDetail::ResultShapeMismatch {
1247                reason,
1248            })),
1249        }
1250    }
1251
1252    /// Construct a query-origin unsupported error preserving one SQL endpoint
1253    /// surface mismatch in structured error detail.
1254    #[cfg(feature = "sql")]
1255    pub(crate) fn query_sql_surface_mismatch(
1256        mismatch: diagnostic_code::SqlSurfaceMismatchCode,
1257    ) -> Self {
1258        Self {
1259            class: ErrorClass::Unsupported,
1260            origin: ErrorOrigin::Query,
1261            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlSurfaceMismatch {
1262                mismatch,
1263            })),
1264        }
1265    }
1266
1267    /// Construct a query-origin unsupported SQL write boundary error.
1268    #[cfg(feature = "sql")]
1269    pub(crate) fn query_sql_write_boundary(
1270        boundary: diagnostic_code::SqlWriteBoundaryCode,
1271    ) -> Self {
1272        Self {
1273            class: ErrorClass::Unsupported,
1274            origin: ErrorOrigin::Query,
1275            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlWriteBoundary {
1276                boundary,
1277            })),
1278        }
1279    }
1280
1281    pub fn store_not_found(_key: impl Sized) -> Self {
1282        Self {
1283            class: ErrorClass::NotFound,
1284            origin: ErrorOrigin::Store,
1285            detail: Some(ErrorDetail::Store(StoreError::NotFound)),
1286        }
1287    }
1288
1289    /// Construct a standardized unsupported-entity-path error.
1290    pub fn unsupported_entity_path(_path: impl Sized) -> Self {
1291        Self::store_unsupported()
1292    }
1293
1294    #[must_use]
1295    pub const fn is_not_found(&self) -> bool {
1296        matches!(self.detail, Some(ErrorDetail::Store(StoreError::NotFound)))
1297    }
1298
1299    /// Construct an index-plan corruption error with a canonical prefix.
1300    #[cold]
1301    #[inline(never)]
1302    pub(crate) fn index_plan_corruption(origin: ErrorOrigin) -> Self {
1303        Self::new(ErrorClass::Corruption, origin)
1304    }
1305
1306    /// Construct an index-plan corruption error for index-origin failures.
1307    #[cold]
1308    #[inline(never)]
1309    pub(crate) fn index_plan_index_corruption() -> Self {
1310        Self::index_plan_corruption(ErrorOrigin::Index)
1311    }
1312
1313    /// Construct an index-plan corruption error for store-origin failures.
1314    #[cold]
1315    #[inline(never)]
1316    pub(crate) fn index_plan_store_corruption() -> Self {
1317        Self::index_plan_corruption(ErrorOrigin::Store)
1318    }
1319
1320    /// Construct an index-plan corruption error for serialize-origin failures.
1321    #[cold]
1322    #[inline(never)]
1323    pub(crate) fn index_plan_serialize_corruption() -> Self {
1324        Self::index_plan_corruption(ErrorOrigin::Serialize)
1325    }
1326
1327    /// Construct an index-plan invariant violation error with a canonical prefix.
1328    #[cfg(test)]
1329    pub(crate) fn index_plan_invariant(origin: ErrorOrigin) -> Self {
1330        Self::new(ErrorClass::InvariantViolation, origin)
1331    }
1332
1333    /// Construct an index-plan invariant violation error for store-origin failures.
1334    #[cfg(test)]
1335    pub(crate) fn index_plan_store_invariant() -> Self {
1336        Self::index_plan_invariant(ErrorOrigin::Store)
1337    }
1338
1339    /// Construct an index uniqueness violation conflict error.
1340    pub(crate) fn index_violation(_path: &str, _index_fields: &[&str]) -> Self {
1341        Self::new(ErrorClass::Conflict, ErrorOrigin::Index)
1342    }
1343}
1344
1345impl fmt::Debug for InternalError {
1346    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1347        fmt_compact_diagnostic(
1348            f,
1349            self.diagnostic_code(),
1350            self.detail
1351                .as_ref()
1352                .and_then(ErrorDetail::diagnostic_detail),
1353        )
1354    }
1355}
1356
1357impl fmt::Display for InternalError {
1358    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1359        f.write_str(self.message())
1360    }
1361}
1362
1363impl std::error::Error for InternalError {}
1364
1365///
1366/// ErrorDetail
1367///
1368/// Structured, origin-specific error detail carried by [`InternalError`].
1369/// This enum is intentionally extensible.
1370///
1371
1372pub enum ErrorDetail {
1373    Store(StoreError),
1374    Query(QueryErrorDetail),
1375    // Future-proofing:
1376    // Index(IndexError),
1377    //
1378    // Executor(ExecutorErrorDetail),
1379}
1380
1381///
1382/// StoreError
1383///
1384/// Store-specific structured error detail.
1385/// Never returned directly; always wrapped in [`ErrorDetail::Store`].
1386///
1387
1388pub enum StoreError {
1389    NotFound,
1390
1391    Corrupt,
1392
1393    InvariantViolation,
1394
1395    SchemaDdlPublicationRaceLost,
1396
1397    SchemaDdlSetNotNullValidationFailed,
1398}
1399
1400///
1401/// QueryErrorDetail
1402///
1403/// Query-origin structured error detail payload.
1404///
1405
1406pub enum QueryErrorDetail {
1407    NumericOverflow,
1408
1409    NumericNotRepresentable,
1410
1411    UnsupportedSqlFeature {
1412        feature: diagnostic_code::SqlFeatureCode,
1413    },
1414
1415    SqlLowering {
1416        reason: diagnostic_code::SqlLoweringCode,
1417    },
1418
1419    UnsupportedProjection {
1420        reason: diagnostic_code::QueryProjectionCode,
1421    },
1422
1423    UnknownAggregateTargetField,
1424
1425    ResultShapeMismatch {
1426        reason: diagnostic_code::QueryResultShapeCode,
1427    },
1428
1429    SqlSurfaceMismatch {
1430        mismatch: diagnostic_code::SqlSurfaceMismatchCode,
1431    },
1432
1433    SqlWriteBoundary {
1434        boundary: diagnostic_code::SqlWriteBoundaryCode,
1435    },
1436
1437    SchemaDdlAdmission {
1438        error: SchemaDdlAdmissionError,
1439    },
1440}
1441
1442impl fmt::Display for QueryErrorDetail {
1443    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1444        f.write_str(COMPACT_QUERY_DIAGNOSTIC_MESSAGE)
1445    }
1446}
1447
1448impl std::error::Error for QueryErrorDetail {}
1449
1450///
1451/// SchemaDdlAdmissionError
1452///
1453/// Stable query-visible SQL DDL admission reason. Human diagnostics may carry
1454/// extra version, fingerprint, and target facts beside this machine-readable
1455/// variant.
1456///
1457
1458#[derive(Clone, Copy, Eq, PartialEq)]
1459pub enum SchemaDdlAdmissionError {
1460    MissingExpectedSchemaVersion,
1461
1462    MissingNextSchemaVersion,
1463
1464    StaleExpectedSchemaVersion,
1465
1466    InvalidExpectedSchemaVersion,
1467
1468    InvalidNextSchemaVersion,
1469
1470    AcceptedSchemaChangeWithoutVersionBump,
1471
1472    EmptyVersionBump,
1473
1474    VersionGap,
1475
1476    VersionRollback,
1477
1478    FingerprintMethodMismatch,
1479
1480    UnsupportedTransitionClass,
1481
1482    PhysicalRunnerMissing,
1483
1484    ValidationFailed,
1485
1486    PublicationRaceLost,
1487
1488    InvalidAddColumnDefault,
1489
1490    InvalidAlterColumnDefault,
1491
1492    GeneratedIndexDropRejected,
1493
1494    RequiredDropDefaultUnsupported,
1495
1496    GeneratedFieldDefaultChangeRejected,
1497
1498    GeneratedFieldNullabilityChangeRejected,
1499
1500    SetNotNullValidationFailed,
1501}
1502
1503impl fmt::Display for SchemaDdlAdmissionError {
1504    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1505        f.write_str(COMPACT_QUERY_DIAGNOSTIC_MESSAGE)
1506    }
1507}
1508
1509impl std::error::Error for SchemaDdlAdmissionError {}
1510
1511impl fmt::Debug for ErrorDetail {
1512    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1513        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
1514    }
1515}
1516
1517impl fmt::Debug for StoreError {
1518    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1519        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
1520    }
1521}
1522
1523impl fmt::Debug for QueryErrorDetail {
1524    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1525        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
1526    }
1527}
1528
1529impl fmt::Debug for SchemaDdlAdmissionError {
1530    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1531        fmt_compact_diagnostic(
1532            f,
1533            diagnostic_code::DiagnosticCode::SchemaDdlAdmission,
1534            Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
1535                reason: self.diagnostic_code(),
1536            }),
1537        )
1538    }
1539}
1540
1541fn fmt_compact_diagnostic(
1542    f: &mut fmt::Formatter<'_>,
1543    code: diagnostic_code::DiagnosticCode,
1544    detail: Option<diagnostic_code::DiagnosticDetail>,
1545) -> fmt::Result {
1546    write!(
1547        f,
1548        "{}",
1549        diagnostic_code::ErrorCode::from_parts(code, detail).raw()
1550    )
1551}
1552
1553impl ErrorDetail {
1554    /// Return the compact diagnostic code for this structured detail.
1555    #[must_use]
1556    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
1557        match self {
1558            Self::Store(error) => error.diagnostic_code(),
1559            Self::Query(error) => error.diagnostic_code(),
1560        }
1561    }
1562
1563    /// Return compact structured diagnostic detail when the payload carries one.
1564    #[must_use]
1565    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
1566        match self {
1567            Self::Store(error) => error.diagnostic_detail(),
1568            Self::Query(error) => error.diagnostic_detail(),
1569        }
1570    }
1571}
1572
1573impl StoreError {
1574    /// Return the compact diagnostic code for this store detail.
1575    #[must_use]
1576    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
1577        match self {
1578            Self::NotFound => diagnostic_code::DiagnosticCode::StoreNotFound,
1579            Self::Corrupt => diagnostic_code::DiagnosticCode::StoreCorruption,
1580            Self::InvariantViolation => diagnostic_code::DiagnosticCode::StoreInvariantViolation,
1581            Self::SchemaDdlPublicationRaceLost | Self::SchemaDdlSetNotNullValidationFailed => {
1582                diagnostic_code::DiagnosticCode::SchemaDdlAdmission
1583            }
1584        }
1585    }
1586
1587    /// Return compact structured diagnostic detail when the store error has one.
1588    #[must_use]
1589    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
1590        match self {
1591            Self::SchemaDdlPublicationRaceLost => {
1592                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
1593                    reason: diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost,
1594                })
1595            }
1596            Self::SchemaDdlSetNotNullValidationFailed => {
1597                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
1598                    reason: diagnostic_code::SchemaDdlAdmissionCode::SetNotNullValidationFailed,
1599                })
1600            }
1601            Self::NotFound | Self::Corrupt | Self::InvariantViolation => None,
1602        }
1603    }
1604}
1605
1606impl QueryErrorDetail {
1607    /// Return the compact diagnostic code for this query detail.
1608    #[must_use]
1609    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
1610        match self {
1611            Self::NumericOverflow => diagnostic_code::DiagnosticCode::QueryNumericOverflow,
1612            Self::NumericNotRepresentable => {
1613                diagnostic_code::DiagnosticCode::QueryNumericNotRepresentable
1614            }
1615            Self::UnsupportedSqlFeature { .. } => {
1616                diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature
1617            }
1618            Self::SqlLowering { .. } => diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature,
1619            Self::UnsupportedProjection { .. } => {
1620                diagnostic_code::DiagnosticCode::QueryUnsupportedProjection
1621            }
1622            Self::UnknownAggregateTargetField => {
1623                diagnostic_code::DiagnosticCode::QueryUnknownAggregateTargetField
1624            }
1625            Self::ResultShapeMismatch { .. } => {
1626                diagnostic_code::DiagnosticCode::QueryResultShapeMismatch
1627            }
1628            Self::SqlSurfaceMismatch { .. } => {
1629                diagnostic_code::DiagnosticCode::QuerySqlSurfaceMismatch
1630            }
1631            Self::SqlWriteBoundary { .. } => diagnostic_code::DiagnosticCode::QuerySqlWriteBoundary,
1632            Self::SchemaDdlAdmission { .. } => diagnostic_code::DiagnosticCode::SchemaDdlAdmission,
1633        }
1634    }
1635
1636    /// Return compact structured diagnostic detail when the query detail has one.
1637    #[must_use]
1638    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
1639        match self {
1640            Self::UnsupportedSqlFeature { feature } => {
1641                Some(diagnostic_code::DiagnosticDetail::UnsupportedSqlFeature { feature: *feature })
1642            }
1643            Self::SqlLowering { reason } => {
1644                Some(diagnostic_code::DiagnosticDetail::SqlLowering { reason: *reason })
1645            }
1646            Self::UnsupportedProjection { reason } => {
1647                Some(diagnostic_code::DiagnosticDetail::QueryProjection { reason: *reason })
1648            }
1649            Self::ResultShapeMismatch { reason } => {
1650                Some(diagnostic_code::DiagnosticDetail::QueryResultShape { reason: *reason })
1651            }
1652            Self::SqlSurfaceMismatch { mismatch } => {
1653                Some(diagnostic_code::DiagnosticDetail::SqlSurfaceMismatch {
1654                    mismatch: *mismatch,
1655                })
1656            }
1657            Self::SqlWriteBoundary { boundary } => {
1658                Some(diagnostic_code::DiagnosticDetail::SqlWriteBoundary {
1659                    boundary: *boundary,
1660                })
1661            }
1662            Self::SchemaDdlAdmission { error } => {
1663                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
1664                    reason: error.diagnostic_code(),
1665                })
1666            }
1667            Self::NumericOverflow
1668            | Self::NumericNotRepresentable
1669            | Self::UnknownAggregateTargetField => None,
1670        }
1671    }
1672}
1673
1674impl SchemaDdlAdmissionError {
1675    /// Return the compact diagnostic code for this SQL DDL admission reason.
1676    #[must_use]
1677    pub const fn diagnostic_code(&self) -> diagnostic_code::SchemaDdlAdmissionCode {
1678        match self {
1679            Self::MissingExpectedSchemaVersion => {
1680                diagnostic_code::SchemaDdlAdmissionCode::MissingExpectedSchemaVersion
1681            }
1682            Self::MissingNextSchemaVersion => {
1683                diagnostic_code::SchemaDdlAdmissionCode::MissingNextSchemaVersion
1684            }
1685            Self::StaleExpectedSchemaVersion => {
1686                diagnostic_code::SchemaDdlAdmissionCode::StaleExpectedSchemaVersion
1687            }
1688            Self::InvalidExpectedSchemaVersion => {
1689                diagnostic_code::SchemaDdlAdmissionCode::InvalidExpectedSchemaVersion
1690            }
1691            Self::InvalidNextSchemaVersion => {
1692                diagnostic_code::SchemaDdlAdmissionCode::InvalidNextSchemaVersion
1693            }
1694            Self::AcceptedSchemaChangeWithoutVersionBump => {
1695                diagnostic_code::SchemaDdlAdmissionCode::AcceptedSchemaChangeWithoutVersionBump
1696            }
1697            Self::EmptyVersionBump => diagnostic_code::SchemaDdlAdmissionCode::EmptyVersionBump,
1698            Self::VersionGap => diagnostic_code::SchemaDdlAdmissionCode::VersionGap,
1699            Self::VersionRollback => diagnostic_code::SchemaDdlAdmissionCode::VersionRollback,
1700            Self::FingerprintMethodMismatch => {
1701                diagnostic_code::SchemaDdlAdmissionCode::FingerprintMethodMismatch
1702            }
1703            Self::UnsupportedTransitionClass => {
1704                diagnostic_code::SchemaDdlAdmissionCode::UnsupportedTransitionClass
1705            }
1706            Self::PhysicalRunnerMissing => {
1707                diagnostic_code::SchemaDdlAdmissionCode::PhysicalRunnerMissing
1708            }
1709            Self::ValidationFailed => diagnostic_code::SchemaDdlAdmissionCode::ValidationFailed,
1710            Self::PublicationRaceLost => {
1711                diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost
1712            }
1713            Self::InvalidAddColumnDefault => {
1714                diagnostic_code::SchemaDdlAdmissionCode::InvalidAddColumnDefault
1715            }
1716            Self::InvalidAlterColumnDefault => {
1717                diagnostic_code::SchemaDdlAdmissionCode::InvalidAlterColumnDefault
1718            }
1719            Self::GeneratedIndexDropRejected => {
1720                diagnostic_code::SchemaDdlAdmissionCode::GeneratedIndexDropRejected
1721            }
1722            Self::RequiredDropDefaultUnsupported => {
1723                diagnostic_code::SchemaDdlAdmissionCode::RequiredDropDefaultUnsupported
1724            }
1725            Self::GeneratedFieldDefaultChangeRejected => {
1726                diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldDefaultChangeRejected
1727            }
1728            Self::GeneratedFieldNullabilityChangeRejected => {
1729                diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldNullabilityChangeRejected
1730            }
1731            Self::SetNotNullValidationFailed => {
1732                diagnostic_code::SchemaDdlAdmissionCode::SetNotNullValidationFailed
1733            }
1734        }
1735    }
1736}
1737
1738///
1739/// ErrorClass
1740/// Internal error taxonomy for runtime classification.
1741/// Not a stable API; may change without notice.
1742///
1743
1744#[repr(u16)]
1745#[derive(Clone, Copy, Eq, PartialEq)]
1746pub enum ErrorClass {
1747    Corruption,
1748    IncompatiblePersistedFormat,
1749    NotFound,
1750    Internal,
1751    Conflict,
1752    Unsupported,
1753    InvariantViolation,
1754}
1755
1756impl ErrorClass {
1757    /// Return a compact diagnostic code for this broad class and origin pair.
1758    #[must_use]
1759    pub const fn diagnostic_code(self, origin: ErrorOrigin) -> diagnostic_code::DiagnosticCode {
1760        match self {
1761            Self::Corruption if matches!(origin, ErrorOrigin::Store) => {
1762                diagnostic_code::DiagnosticCode::StoreCorruption
1763            }
1764            Self::Corruption => diagnostic_code::DiagnosticCode::RuntimeCorruption,
1765            Self::IncompatiblePersistedFormat => {
1766                diagnostic_code::DiagnosticCode::RuntimeIncompatiblePersistedFormat
1767            }
1768            Self::NotFound if matches!(origin, ErrorOrigin::Store) => {
1769                diagnostic_code::DiagnosticCode::StoreNotFound
1770            }
1771            Self::NotFound => diagnostic_code::DiagnosticCode::RuntimeNotFound,
1772            Self::Internal => diagnostic_code::DiagnosticCode::RuntimeInternal,
1773            Self::Conflict => diagnostic_code::DiagnosticCode::RuntimeConflict,
1774            Self::Unsupported => diagnostic_code::DiagnosticCode::RuntimeUnsupported,
1775            Self::InvariantViolation if matches!(origin, ErrorOrigin::Store) => {
1776                diagnostic_code::DiagnosticCode::StoreInvariantViolation
1777            }
1778            Self::InvariantViolation => diagnostic_code::DiagnosticCode::RuntimeInvariantViolation,
1779        }
1780    }
1781}
1782
1783impl fmt::Debug for ErrorClass {
1784    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1785        write!(f, "{}", *self as u16)
1786    }
1787}
1788
1789///
1790/// ErrorOrigin
1791/// Internal origin taxonomy for runtime classification.
1792/// Not a stable API; may change without notice.
1793///
1794
1795#[repr(u16)]
1796#[derive(Clone, Copy, Eq, PartialEq)]
1797pub enum ErrorOrigin {
1798    Serialize,
1799    Store,
1800    Index,
1801    Identity,
1802    Query,
1803    Planner,
1804    Cursor,
1805    Recovery,
1806    Response,
1807    Executor,
1808    Interface,
1809}
1810
1811impl ErrorOrigin {
1812    /// Return the compact diagnostic origin for this internal origin.
1813    #[must_use]
1814    pub const fn diagnostic_origin(self) -> diagnostic_code::ErrorOrigin {
1815        match self {
1816            Self::Serialize => diagnostic_code::ErrorOrigin::Serialize,
1817            Self::Store => diagnostic_code::ErrorOrigin::Store,
1818            Self::Index => diagnostic_code::ErrorOrigin::Index,
1819            Self::Identity => diagnostic_code::ErrorOrigin::Identity,
1820            Self::Query => diagnostic_code::ErrorOrigin::Query,
1821            Self::Planner => diagnostic_code::ErrorOrigin::Planner,
1822            Self::Cursor => diagnostic_code::ErrorOrigin::Cursor,
1823            Self::Recovery => diagnostic_code::ErrorOrigin::Recovery,
1824            Self::Response => diagnostic_code::ErrorOrigin::Response,
1825            Self::Executor => diagnostic_code::ErrorOrigin::Executor,
1826            Self::Interface => diagnostic_code::ErrorOrigin::Interface,
1827        }
1828    }
1829}
1830
1831impl fmt::Debug for ErrorOrigin {
1832    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1833        write!(f, "{}", *self as u16)
1834    }
1835}