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