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