Skip to main content

icydb_diagnostic_code/
lib.rs

1//! Compact diagnostic identity for IcyDB.
2//!
3//! This crate intentionally contains no rich diagnostic prose or Candid wire
4//! types. Production canister builds collapse diagnostics to numeric wire
5//! codes before they cross the public canister boundary.
6
7///
8/// DiagnosticCode
9///
10/// Stable machine-readable diagnostic reason.
11///
12
13#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
14pub enum DiagnosticCode {
15    QueryValidate,
16    QueryIntent,
17    QueryPlan,
18    QueryAccessRequirement,
19    QueryUnorderedPagination,
20    QueryInvalidContinuationCursor,
21    QueryNotFound,
22    QueryNotUnique,
23    QueryNumericOverflow,
24    QueryNumericNotRepresentable,
25    QueryUnsupportedSqlFeature,
26    QuerySqlSurfaceMismatch,
27    SchemaDdlAdmission,
28    StoreNotFound,
29    StoreCorruption,
30    StoreInvariantViolation,
31    RuntimeCorruption,
32    RuntimeIncompatiblePersistedFormat,
33    RuntimeInvariantViolation,
34    RuntimeConflict,
35    RuntimeNotFound,
36    RuntimeUnsupported,
37    RuntimeInternal,
38}
39
40impl DiagnosticCode {
41    /// Return the broad diagnostic class for this code.
42    #[must_use]
43    pub const fn class(self) -> ErrorClass {
44        match self {
45            Self::StoreCorruption | Self::RuntimeCorruption => ErrorClass::Corruption,
46            Self::RuntimeIncompatiblePersistedFormat => ErrorClass::IncompatiblePersistedFormat,
47            Self::QueryNotFound | Self::StoreNotFound | Self::RuntimeNotFound => {
48                ErrorClass::NotFound
49            }
50            Self::RuntimeConflict => ErrorClass::Conflict,
51            Self::QueryUnsupportedSqlFeature
52            | Self::QuerySqlSurfaceMismatch
53            | Self::RuntimeUnsupported => ErrorClass::Unsupported,
54            Self::StoreInvariantViolation | Self::RuntimeInvariantViolation => {
55                ErrorClass::InvariantViolation
56            }
57            Self::RuntimeInternal => ErrorClass::Internal,
58            Self::QueryValidate
59            | Self::QueryIntent
60            | Self::QueryPlan
61            | Self::QueryAccessRequirement
62            | Self::QueryUnorderedPagination
63            | Self::QueryInvalidContinuationCursor
64            | Self::QueryNotUnique
65            | Self::QueryNumericOverflow
66            | Self::QueryNumericNotRepresentable
67            | Self::SchemaDdlAdmission => ErrorClass::Query,
68        }
69    }
70
71    /// Return the default diagnostic origin for this code.
72    #[must_use]
73    pub const fn origin(self) -> ErrorOrigin {
74        match self {
75            Self::StoreNotFound | Self::StoreCorruption | Self::StoreInvariantViolation => {
76                ErrorOrigin::Store
77            }
78            Self::RuntimeCorruption
79            | Self::RuntimeIncompatiblePersistedFormat
80            | Self::RuntimeInvariantViolation
81            | Self::RuntimeConflict
82            | Self::RuntimeNotFound
83            | Self::RuntimeUnsupported
84            | Self::RuntimeInternal => ErrorOrigin::Runtime,
85            Self::QueryValidate
86            | Self::QueryIntent
87            | Self::QueryPlan
88            | Self::QueryAccessRequirement
89            | Self::QueryUnorderedPagination
90            | Self::QueryInvalidContinuationCursor
91            | Self::QueryNotFound
92            | Self::QueryNotUnique
93            | Self::QueryNumericOverflow
94            | Self::QueryNumericNotRepresentable
95            | Self::QueryUnsupportedSqlFeature
96            | Self::QuerySqlSurfaceMismatch
97            | Self::SchemaDdlAdmission => ErrorOrigin::Query,
98        }
99    }
100
101    /// Return the compact public wire code for this broad diagnostic reason.
102    #[must_use]
103    pub const fn error_code(self) -> ErrorCode {
104        match self {
105            Self::QueryValidate => ErrorCode::QUERY_VALIDATE,
106            Self::QueryIntent => ErrorCode::QUERY_INTENT,
107            Self::QueryPlan => ErrorCode::QUERY_PLAN,
108            Self::QueryAccessRequirement => ErrorCode::QUERY_ACCESS_REQUIREMENT,
109            Self::QueryUnorderedPagination => ErrorCode::QUERY_UNORDERED_PAGINATION,
110            Self::QueryInvalidContinuationCursor => ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
111            Self::QueryNotFound => ErrorCode::QUERY_NOT_FOUND,
112            Self::QueryNotUnique => ErrorCode::QUERY_NOT_UNIQUE,
113            Self::QueryNumericOverflow => ErrorCode::QUERY_NUMERIC_OVERFLOW,
114            Self::QueryNumericNotRepresentable => ErrorCode::QUERY_NUMERIC_NOT_REPRESENTABLE,
115            Self::QueryUnsupportedSqlFeature => ErrorCode::QUERY_UNSUPPORTED_SQL_FEATURE,
116            Self::QuerySqlSurfaceMismatch => ErrorCode::QUERY_SQL_SURFACE_MISMATCH,
117            Self::SchemaDdlAdmission => ErrorCode::SCHEMA_DDL_ADMISSION,
118            Self::StoreNotFound => ErrorCode::STORE_NOT_FOUND,
119            Self::StoreCorruption => ErrorCode::STORE_CORRUPTION,
120            Self::StoreInvariantViolation => ErrorCode::STORE_INVARIANT_VIOLATION,
121            Self::RuntimeCorruption => ErrorCode::RUNTIME_CORRUPTION,
122            Self::RuntimeIncompatiblePersistedFormat => {
123                ErrorCode::RUNTIME_INCOMPATIBLE_PERSISTED_FORMAT
124            }
125            Self::RuntimeInvariantViolation => ErrorCode::RUNTIME_INVARIANT_VIOLATION,
126            Self::RuntimeConflict => ErrorCode::RUNTIME_CONFLICT,
127            Self::RuntimeNotFound => ErrorCode::RUNTIME_NOT_FOUND,
128            Self::RuntimeUnsupported => ErrorCode::RUNTIME_UNSUPPORTED,
129            Self::RuntimeInternal => ErrorCode::RUNTIME_INTERNAL,
130        }
131    }
132}
133
134///
135/// ErrorCode
136///
137/// Stable numeric public error identity.
138///
139/// The public Candid `icydb::Error` stores this value as `nat16` so canister
140/// interfaces do not retain rich diagnostic enum labels. Rich diagnostics can
141/// still be reconstructed by host-side tooling from this leaf code. Before
142/// 1.0.0, the code space is hard-cut to a single compact sequential range.
143///
144
145#[derive(Clone, Copy, Eq, Hash, PartialEq)]
146pub struct ErrorCode(u16);
147
148impl ErrorCode {
149    pub const QUERY_VALIDATE: Self = Self(1);
150    pub const QUERY_INTENT: Self = Self(2);
151    pub const QUERY_PLAN: Self = Self(3);
152    pub const QUERY_ACCESS_REQUIREMENT: Self = Self(4);
153    pub const QUERY_UNORDERED_PAGINATION: Self = Self(5);
154    pub const QUERY_INVALID_CONTINUATION_CURSOR: Self = Self(6);
155    pub const QUERY_NOT_FOUND: Self = Self(7);
156    pub const QUERY_NOT_UNIQUE: Self = Self(8);
157    pub const QUERY_NUMERIC_OVERFLOW: Self = Self(9);
158    pub const QUERY_NUMERIC_NOT_REPRESENTABLE: Self = Self(10);
159    pub const QUERY_UNSUPPORTED_SQL_FEATURE: Self = Self(11);
160    pub const QUERY_SQL_SURFACE_MISMATCH: Self = Self(12);
161    pub const SCHEMA_DDL_ADMISSION: Self = Self(13);
162    pub const STORE_NOT_FOUND: Self = Self(14);
163    pub const STORE_CORRUPTION: Self = Self(15);
164    pub const STORE_INVARIANT_VIOLATION: Self = Self(16);
165    pub const RUNTIME_CORRUPTION: Self = Self(17);
166    pub const RUNTIME_INCOMPATIBLE_PERSISTED_FORMAT: Self = Self(18);
167    pub const RUNTIME_INVARIANT_VIOLATION: Self = Self(19);
168    pub const RUNTIME_CONFLICT: Self = Self(20);
169    pub const RUNTIME_NOT_FOUND: Self = Self(21);
170    pub const RUNTIME_UNSUPPORTED: Self = Self(22);
171    pub const RUNTIME_INTERNAL: Self = Self(23);
172
173    pub const RUNTIME_BOUNDARY_SQL_SURFACE_CONTROLLER_REQUIRED: Self = Self(24);
174    pub const RUNTIME_BOUNDARY_SCHEMA_SURFACE_CONTROLLER_REQUIRED: Self = Self(25);
175    pub const RUNTIME_BOUNDARY_SQL_QUERY_NO_CONFIGURED_ENTITIES: Self = Self(26);
176    pub const RUNTIME_BOUNDARY_SQL_QUERY_ENTITY_NOT_CONFIGURED: Self = Self(27);
177    pub const RUNTIME_BOUNDARY_SQL_DDL_TARGET_REQUIRED: Self = Self(28);
178    pub const RUNTIME_BOUNDARY_SQL_DDL_ENTITY_NOT_CONFIGURED: Self = Self(29);
179    pub const RUNTIME_BOUNDARY_QUERY_RESPONSE_ROWS_REQUIRED: Self = Self(30);
180    pub const RUNTIME_BOUNDARY_QUERY_RESPONSE_GROUPED_ROWS_REQUIRED: Self = Self(31);
181    pub const RUNTIME_BOUNDARY_MUTATION_RESULT_ENTITY_REQUIRED: Self = Self(32);
182    pub const RUNTIME_BOUNDARY_MUTATION_RESULT_ENTITIES_REQUIRED: Self = Self(33);
183    pub const RUNTIME_BOUNDARY_MUTATION_RESULT_ID_REQUIRED: Self = Self(34);
184    pub const RUNTIME_BOUNDARY_MUTATION_RESULT_IDS_REQUIRED: Self = Self(35);
185    pub const RUNTIME_BOUNDARY_ROW_PROJECTION_FIELD_NOT_CONFIGURED: Self = Self(36);
186
187    pub const SQL_FEATURE_AGGREGATE_FILTER_CLAUSE: Self = Self(37);
188    pub const SQL_FEATURE_ALTER_STATEMENT_BEYOND_ALTER_TABLE: Self = Self(38);
189    pub const SQL_FEATURE_ALTER_TABLE_ADD_COLUMN_DUPLICATE_DEFAULT: Self = Self(39);
190    pub const SQL_FEATURE_ALTER_TABLE_ADD_COLUMN_MODIFIERS: Self = Self(40);
191    pub const SQL_FEATURE_ALTER_TABLE_ADD_STATEMENT_BEYOND_ADD_COLUMN: Self = Self(41);
192    pub const SQL_FEATURE_ALTER_TABLE_ALTER_COLUMN_DROP_UNSUPPORTED_ACTION: Self = Self(42);
193    pub const SQL_FEATURE_ALTER_TABLE_ALTER_COLUMN_MODIFIERS: Self = Self(43);
194    pub const SQL_FEATURE_ALTER_TABLE_ALTER_COLUMN_SET_UNSUPPORTED_ACTION: Self = Self(44);
195    pub const SQL_FEATURE_ALTER_TABLE_ALTER_COLUMN_UNSUPPORTED_ACTION: Self = Self(45);
196    pub const SQL_FEATURE_ALTER_TABLE_ALTER_STATEMENT_BEYOND_ALTER_COLUMN: Self = Self(46);
197    pub const SQL_FEATURE_ALTER_TABLE_DROP_COLUMN_IF_EXISTS_SYNTAX: Self = Self(47);
198    pub const SQL_FEATURE_ALTER_TABLE_DROP_COLUMN_MODIFIERS: Self = Self(48);
199    pub const SQL_FEATURE_ALTER_TABLE_DROP_STATEMENT_BEYOND_DROP_COLUMN: Self = Self(49);
200    pub const SQL_FEATURE_ALTER_TABLE_RENAME_COLUMN_MISSING_TO: Self = Self(50);
201    pub const SQL_FEATURE_ALTER_TABLE_RENAME_COLUMN_MODIFIERS: Self = Self(51);
202    pub const SQL_FEATURE_ALTER_TABLE_RENAME_STATEMENT_BEYOND_RENAME_COLUMN: Self = Self(52);
203    pub const SQL_FEATURE_ALTER_TABLE_UNSUPPORTED_OPERATION: Self = Self(53);
204    pub const SQL_FEATURE_COLUMN_ALIAS: Self = Self(54);
205    pub const SQL_FEATURE_CREATE_INDEX_IF_NOT_EXISTS_SYNTAX: Self = Self(55);
206    pub const SQL_FEATURE_CREATE_INDEX_KEY_ORDERING_MODIFIERS: Self = Self(56);
207    pub const SQL_FEATURE_CREATE_INDEX_MODIFIERS: Self = Self(57);
208    pub const SQL_FEATURE_CREATE_STATEMENT_BEYOND_CREATE_INDEX: Self = Self(58);
209    pub const SQL_FEATURE_DESCRIBE_MODIFIER: Self = Self(59);
210    pub const SQL_FEATURE_DDL_SCHEMA_VERSION_DUPLICATE_EXPECTED_CLAUSE: Self = Self(60);
211    pub const SQL_FEATURE_DDL_SCHEMA_VERSION_DUPLICATE_SET_CLAUSE: Self = Self(61);
212    pub const SQL_FEATURE_DROP_INDEX_MODIFIERS: Self = Self(62);
213    pub const SQL_FEATURE_DROP_INDEX_IF_EXISTS_SYNTAX: Self = Self(63);
214    pub const SQL_FEATURE_DROP_STATEMENT_BEYOND_DROP_INDEX: Self = Self(64);
215    pub const SQL_FEATURE_EXPRESSION_INDEX_UNSUPPORTED_FUNCTION: Self = Self(65);
216    pub const SQL_FEATURE_HAVING: Self = Self(66);
217    pub const SQL_FEATURE_INSERT: Self = Self(67);
218    pub const SQL_FEATURE_JOIN: Self = Self(68);
219    pub const SQL_FEATURE_LIKE_PATTERN_BEYOND_TRAILING_PREFIX: Self = Self(69);
220    pub const SQL_FEATURE_LOWER_FIELD_PREDICATE_UNSUPPORTED: Self = Self(70);
221    pub const SQL_FEATURE_MULTI_STATEMENT_SQL: Self = Self(71);
222    pub const SQL_FEATURE_NESTED_AGGREGATE_INPUT: Self = Self(72);
223    pub const SQL_FEATURE_NESTED_PROJECTION_FUNCTION_IN_ARITHMETIC: Self = Self(73);
224    pub const SQL_FEATURE_ORDER_BY_UNSUPPORTED_FORM: Self = Self(74);
225    pub const SQL_FEATURE_OTHER: Self = Self(75);
226    pub const SQL_FEATURE_PARAMETER_BINDING: Self = Self(76);
227    pub const SQL_FEATURE_PARAMETERIZED_SCHEMA_VERSION: Self = Self(77);
228    pub const SQL_FEATURE_PREDICATE_STARTS_WITH_FIRST_ARGUMENT: Self = Self(78);
229    pub const SQL_FEATURE_QUOTED_IDENTIFIERS: Self = Self(79);
230    pub const SQL_FEATURE_RETURNING_UNSUPPORTED_SHAPE: Self = Self(80);
231    pub const SQL_FEATURE_SCALAR_FUNCTION_EXPRESSION_POSITION: Self = Self(81);
232    pub const SQL_FEATURE_SCALE_TAKING_NUMERIC_FUNCTION_EXPRESSION_POSITION: Self = Self(82);
233    pub const SQL_FEATURE_SEARCHED_CASE_GROUPED_ORDER_BY: Self = Self(83);
234    pub const SQL_FEATURE_SHOW_COLUMNS_MODIFIERS: Self = Self(84);
235    pub const SQL_FEATURE_SHOW_ENTITIES_MODIFIERS: Self = Self(85);
236    pub const SQL_FEATURE_SHOW_INDEXES_MODIFIERS: Self = Self(86);
237    pub const SQL_FEATURE_SHOW_MEMORY_MODIFIERS: Self = Self(87);
238    pub const SQL_FEATURE_SHOW_STORES_MODIFIERS: Self = Self(88);
239    pub const SQL_FEATURE_SHOW_UNSUPPORTED_COMMAND: Self = Self(89);
240    pub const SQL_FEATURE_SIMPLE_CASE_EXPRESSION: Self = Self(90);
241    pub const SQL_FEATURE_STANDALONE_LITERAL_PROJECTION_ITEM: Self = Self(91);
242    pub const SQL_FEATURE_SUPPORTED_GROUPED_ORDER_BY_EXPRESSION_FAMILY: Self = Self(92);
243    pub const SQL_FEATURE_SUPPORTED_ORDER_BY_EXPRESSION_FAMILY: Self = Self(93);
244    pub const SQL_FEATURE_UNION_INTERSECT_EXCEPT: Self = Self(94);
245    pub const SQL_FEATURE_UNSUPPORTED_FUNCTION_NAMESPACE: Self = Self(95);
246    pub const SQL_FEATURE_UPDATE: Self = Self(96);
247    pub const SQL_FEATURE_UPPER_FIELD_PREDICATE_UNSUPPORTED: Self = Self(97);
248    pub const SQL_FEATURE_WINDOW_FUNCTION: Self = Self(98);
249    pub const SQL_FEATURE_WITH: Self = Self(99);
250
251    const SQL_FEATURE_DETAILS: [SqlFeatureCode; 63] = [
252        SqlFeatureCode::AggregateFilterClause,
253        SqlFeatureCode::AlterStatementBeyondAlterTable,
254        SqlFeatureCode::AlterTableAddColumnDuplicateDefault,
255        SqlFeatureCode::AlterTableAddColumnModifiers,
256        SqlFeatureCode::AlterTableAddStatementBeyondAddColumn,
257        SqlFeatureCode::AlterTableAlterColumnDropUnsupportedAction,
258        SqlFeatureCode::AlterTableAlterColumnModifiers,
259        SqlFeatureCode::AlterTableAlterColumnSetUnsupportedAction,
260        SqlFeatureCode::AlterTableAlterColumnUnsupportedAction,
261        SqlFeatureCode::AlterTableAlterStatementBeyondAlterColumn,
262        SqlFeatureCode::AlterTableDropColumnIfExistsSyntax,
263        SqlFeatureCode::AlterTableDropColumnModifiers,
264        SqlFeatureCode::AlterTableDropStatementBeyondDropColumn,
265        SqlFeatureCode::AlterTableRenameColumnMissingTo,
266        SqlFeatureCode::AlterTableRenameColumnModifiers,
267        SqlFeatureCode::AlterTableRenameStatementBeyondRenameColumn,
268        SqlFeatureCode::AlterTableUnsupportedOperation,
269        SqlFeatureCode::ColumnAlias,
270        SqlFeatureCode::CreateIndexIfNotExistsSyntax,
271        SqlFeatureCode::CreateIndexKeyOrderingModifiers,
272        SqlFeatureCode::CreateIndexModifiers,
273        SqlFeatureCode::CreateStatementBeyondCreateIndex,
274        SqlFeatureCode::DescribeModifier,
275        SqlFeatureCode::DdlSchemaVersionDuplicateExpectedClause,
276        SqlFeatureCode::DdlSchemaVersionDuplicateSetClause,
277        SqlFeatureCode::DropIndexModifiers,
278        SqlFeatureCode::DropIndexIfExistsSyntax,
279        SqlFeatureCode::DropStatementBeyondDropIndex,
280        SqlFeatureCode::ExpressionIndexUnsupportedFunction,
281        SqlFeatureCode::Having,
282        SqlFeatureCode::Insert,
283        SqlFeatureCode::Join,
284        SqlFeatureCode::LikePatternBeyondTrailingPrefix,
285        SqlFeatureCode::LowerFieldPredicateUnsupported,
286        SqlFeatureCode::MultiStatementSql,
287        SqlFeatureCode::NestedAggregateInput,
288        SqlFeatureCode::NestedProjectionFunctionInArithmetic,
289        SqlFeatureCode::OrderByUnsupportedForm,
290        SqlFeatureCode::Other,
291        SqlFeatureCode::ParameterBinding,
292        SqlFeatureCode::ParameterizedSchemaVersion,
293        SqlFeatureCode::PredicateStartsWithFirstArgument,
294        SqlFeatureCode::QuotedIdentifiers,
295        SqlFeatureCode::ReturningUnsupportedShape,
296        SqlFeatureCode::ScalarFunctionExpressionPosition,
297        SqlFeatureCode::ScaleTakingNumericFunctionExpressionPosition,
298        SqlFeatureCode::SearchedCaseGroupedOrderBy,
299        SqlFeatureCode::ShowColumnsModifiers,
300        SqlFeatureCode::ShowEntitiesModifiers,
301        SqlFeatureCode::ShowIndexesModifiers,
302        SqlFeatureCode::ShowMemoryModifiers,
303        SqlFeatureCode::ShowStoresModifiers,
304        SqlFeatureCode::ShowUnsupportedCommand,
305        SqlFeatureCode::SimpleCaseExpression,
306        SqlFeatureCode::StandaloneLiteralProjectionItem,
307        SqlFeatureCode::SupportedGroupedOrderByExpressionFamily,
308        SqlFeatureCode::SupportedOrderByExpressionFamily,
309        SqlFeatureCode::UnionIntersectExcept,
310        SqlFeatureCode::UnsupportedFunctionNamespace,
311        SqlFeatureCode::Update,
312        SqlFeatureCode::UpperFieldPredicateUnsupported,
313        SqlFeatureCode::WindowFunction,
314        SqlFeatureCode::With,
315    ];
316
317    pub const SQL_SURFACE_QUERY_REJECTS_INSERT: Self = Self(100);
318    pub const SQL_SURFACE_QUERY_REJECTS_UPDATE: Self = Self(101);
319    pub const SQL_SURFACE_QUERY_REJECTS_DELETE: Self = Self(102);
320    pub const SQL_SURFACE_UPDATE_REJECTS_SELECT: Self = Self(103);
321    pub const SQL_SURFACE_UPDATE_REJECTS_EXPLAIN: Self = Self(104);
322    pub const SQL_SURFACE_UPDATE_REJECTS_DESCRIBE: Self = Self(105);
323    pub const SQL_SURFACE_UPDATE_REJECTS_SHOW_INDEXES: Self = Self(106);
324    pub const SQL_SURFACE_UPDATE_REJECTS_SHOW_COLUMNS: Self = Self(107);
325    pub const SQL_SURFACE_UPDATE_REJECTS_SHOW_ENTITIES: Self = Self(108);
326    pub const SQL_SURFACE_UPDATE_REJECTS_SHOW_STORES: Self = Self(109);
327    pub const SQL_SURFACE_UPDATE_REJECTS_SHOW_MEMORY: Self = Self(110);
328
329    pub const SCHEMA_DDL_MISSING_EXPECTED_SCHEMA_VERSION: Self = Self(111);
330    pub const SCHEMA_DDL_MISSING_NEXT_SCHEMA_VERSION: Self = Self(112);
331    pub const SCHEMA_DDL_STALE_EXPECTED_SCHEMA_VERSION: Self = Self(113);
332    pub const SCHEMA_DDL_INVALID_EXPECTED_SCHEMA_VERSION: Self = Self(114);
333    pub const SCHEMA_DDL_INVALID_NEXT_SCHEMA_VERSION: Self = Self(115);
334    pub const SCHEMA_DDL_ACCEPTED_SCHEMA_CHANGE_WITHOUT_VERSION_BUMP: Self = Self(116);
335    pub const SCHEMA_DDL_EMPTY_VERSION_BUMP: Self = Self(117);
336    pub const SCHEMA_DDL_VERSION_GAP: Self = Self(118);
337    pub const SCHEMA_DDL_VERSION_ROLLBACK: Self = Self(119);
338    pub const SCHEMA_DDL_FINGERPRINT_METHOD_MISMATCH: Self = Self(120);
339    pub const SCHEMA_DDL_UNSUPPORTED_TRANSITION_CLASS: Self = Self(121);
340    pub const SCHEMA_DDL_PHYSICAL_RUNNER_MISSING: Self = Self(122);
341    pub const SCHEMA_DDL_VALIDATION_FAILED: Self = Self(123);
342    pub const SCHEMA_DDL_PUBLICATION_RACE_LOST: Self = Self(124);
343
344    /// Build an error code from its raw public wire value.
345    #[must_use]
346    pub const fn from_raw(raw: u16) -> Self {
347        Self(raw)
348    }
349
350    /// Return the raw public wire value.
351    #[must_use]
352    pub const fn raw(self) -> u16 {
353        self.0
354    }
355
356    /// Collapse a rich diagnostic into one public leaf code.
357    #[must_use]
358    pub const fn from_parts(code: DiagnosticCode, detail: Option<DiagnosticDetail>) -> Self {
359        match detail {
360            Some(DiagnosticDetail::QueryKind { kind }) => Self::from_query_kind(kind),
361            Some(DiagnosticDetail::RuntimeKind { kind }) => Self::from_runtime_kind(kind),
362            Some(DiagnosticDetail::RuntimeBoundary { boundary }) => {
363                Self::from_runtime_boundary(boundary)
364            }
365            Some(DiagnosticDetail::SchemaDdlAdmission { reason }) => Self::from_schema_ddl(reason),
366            Some(DiagnosticDetail::UnsupportedSqlFeature { feature }) => {
367                Self::from_sql_feature(feature)
368            }
369            Some(DiagnosticDetail::SqlSurfaceMismatch { mismatch }) => {
370                Self::from_sql_surface_mismatch(mismatch)
371            }
372            None => code.error_code(),
373        }
374    }
375
376    /// Return the broad diagnostic reason represented by this public code.
377    #[must_use]
378    pub const fn diagnostic_code(self) -> DiagnosticCode {
379        match self.raw() {
380            1 => DiagnosticCode::QueryValidate,
381            2 => DiagnosticCode::QueryIntent,
382            3 => DiagnosticCode::QueryPlan,
383            4 => DiagnosticCode::QueryAccessRequirement,
384            5 => DiagnosticCode::QueryUnorderedPagination,
385            6 => DiagnosticCode::QueryInvalidContinuationCursor,
386            7 => DiagnosticCode::QueryNotFound,
387            8 => DiagnosticCode::QueryNotUnique,
388            9 => DiagnosticCode::QueryNumericOverflow,
389            10 => DiagnosticCode::QueryNumericNotRepresentable,
390            11 | 37..=99 => DiagnosticCode::QueryUnsupportedSqlFeature,
391            12 | 100..=110 => DiagnosticCode::QuerySqlSurfaceMismatch,
392            13 | 111..=124 => DiagnosticCode::SchemaDdlAdmission,
393            14 => DiagnosticCode::StoreNotFound,
394            15 => DiagnosticCode::StoreCorruption,
395            16 => DiagnosticCode::StoreInvariantViolation,
396            17 => DiagnosticCode::RuntimeCorruption,
397            18 => DiagnosticCode::RuntimeIncompatiblePersistedFormat,
398            19 => DiagnosticCode::RuntimeInvariantViolation,
399            20 => DiagnosticCode::RuntimeConflict,
400            21 => DiagnosticCode::RuntimeNotFound,
401            22 | 24..=36 => DiagnosticCode::RuntimeUnsupported,
402            _ => DiagnosticCode::RuntimeInternal,
403        }
404    }
405
406    /// Return the diagnostic class represented by this public code.
407    #[must_use]
408    pub const fn class(self) -> ErrorClass {
409        self.diagnostic_code().class()
410    }
411
412    /// Reconstruct rich diagnostic detail for host-side rendering, when known.
413    #[must_use]
414    pub const fn diagnostic_detail(self) -> Option<DiagnosticDetail> {
415        match self.raw() {
416            1..=8 => Self::query_kind_detail(self.raw()),
417            17..=23 => Self::runtime_kind_detail(self.raw()),
418            24..=36 => Self::runtime_boundary_detail(self.raw()),
419            37..=99 => Self::sql_feature_detail(self.raw()),
420            100..=110 => Self::sql_surface_detail(self.raw()),
421            111..=124 => Self::schema_ddl_detail(self.raw()),
422            _ => None,
423        }
424    }
425
426    /// Reconstruct a rich diagnostic payload for host-side rendering.
427    #[must_use]
428    pub const fn diagnostic(self, origin: ErrorOrigin) -> Diagnostic {
429        Diagnostic::new(self.diagnostic_code(), origin, self.diagnostic_detail())
430    }
431
432    const fn from_query_kind(kind: QueryErrorKind) -> Self {
433        match kind {
434            QueryErrorKind::Validate => Self::QUERY_VALIDATE,
435            QueryErrorKind::Intent => Self::QUERY_INTENT,
436            QueryErrorKind::Plan => Self::QUERY_PLAN,
437            QueryErrorKind::AccessRequirement => Self::QUERY_ACCESS_REQUIREMENT,
438            QueryErrorKind::UnorderedPagination => Self::QUERY_UNORDERED_PAGINATION,
439            QueryErrorKind::InvalidContinuationCursor => Self::QUERY_INVALID_CONTINUATION_CURSOR,
440            QueryErrorKind::NotFound => Self::QUERY_NOT_FOUND,
441            QueryErrorKind::NotUnique => Self::QUERY_NOT_UNIQUE,
442        }
443    }
444
445    const fn from_runtime_kind(kind: RuntimeErrorKind) -> Self {
446        match kind {
447            RuntimeErrorKind::Corruption => Self::RUNTIME_CORRUPTION,
448            RuntimeErrorKind::IncompatiblePersistedFormat => {
449                Self::RUNTIME_INCOMPATIBLE_PERSISTED_FORMAT
450            }
451            RuntimeErrorKind::InvariantViolation => Self::RUNTIME_INVARIANT_VIOLATION,
452            RuntimeErrorKind::Conflict => Self::RUNTIME_CONFLICT,
453            RuntimeErrorKind::NotFound => Self::RUNTIME_NOT_FOUND,
454            RuntimeErrorKind::Unsupported => Self::RUNTIME_UNSUPPORTED,
455            RuntimeErrorKind::Internal => Self::RUNTIME_INTERNAL,
456        }
457    }
458
459    const fn from_runtime_boundary(boundary: RuntimeBoundaryCode) -> Self {
460        Self(Self::RUNTIME_BOUNDARY_SQL_SURFACE_CONTROLLER_REQUIRED.raw() + boundary as u16)
461    }
462
463    const fn from_sql_feature(feature: SqlFeatureCode) -> Self {
464        Self(Self::SQL_FEATURE_AGGREGATE_FILTER_CLAUSE.raw() + feature as u16)
465    }
466
467    const fn from_sql_surface_mismatch(mismatch: SqlSurfaceMismatchCode) -> Self {
468        Self(Self::SQL_SURFACE_QUERY_REJECTS_INSERT.raw() + mismatch as u16)
469    }
470
471    const fn from_schema_ddl(reason: SchemaDdlAdmissionCode) -> Self {
472        Self(Self::SCHEMA_DDL_MISSING_EXPECTED_SCHEMA_VERSION.raw() + reason as u16)
473    }
474
475    const fn query_kind_detail(raw: u16) -> Option<DiagnosticDetail> {
476        match raw {
477            1 => Some(DiagnosticDetail::QueryKind {
478                kind: QueryErrorKind::Validate,
479            }),
480            2 => Some(DiagnosticDetail::QueryKind {
481                kind: QueryErrorKind::Intent,
482            }),
483            3 => Some(DiagnosticDetail::QueryKind {
484                kind: QueryErrorKind::Plan,
485            }),
486            4 => Some(DiagnosticDetail::QueryKind {
487                kind: QueryErrorKind::AccessRequirement,
488            }),
489            5 => Some(DiagnosticDetail::QueryKind {
490                kind: QueryErrorKind::UnorderedPagination,
491            }),
492            6 => Some(DiagnosticDetail::QueryKind {
493                kind: QueryErrorKind::InvalidContinuationCursor,
494            }),
495            7 => Some(DiagnosticDetail::QueryKind {
496                kind: QueryErrorKind::NotFound,
497            }),
498            8 => Some(DiagnosticDetail::QueryKind {
499                kind: QueryErrorKind::NotUnique,
500            }),
501            _ => None,
502        }
503    }
504
505    const fn runtime_kind_detail(raw: u16) -> Option<DiagnosticDetail> {
506        match raw {
507            17 => Some(DiagnosticDetail::RuntimeKind {
508                kind: RuntimeErrorKind::Corruption,
509            }),
510            18 => Some(DiagnosticDetail::RuntimeKind {
511                kind: RuntimeErrorKind::IncompatiblePersistedFormat,
512            }),
513            19 => Some(DiagnosticDetail::RuntimeKind {
514                kind: RuntimeErrorKind::InvariantViolation,
515            }),
516            20 => Some(DiagnosticDetail::RuntimeKind {
517                kind: RuntimeErrorKind::Conflict,
518            }),
519            21 => Some(DiagnosticDetail::RuntimeKind {
520                kind: RuntimeErrorKind::NotFound,
521            }),
522            22 => Some(DiagnosticDetail::RuntimeKind {
523                kind: RuntimeErrorKind::Unsupported,
524            }),
525            23 => Some(DiagnosticDetail::RuntimeKind {
526                kind: RuntimeErrorKind::Internal,
527            }),
528            _ => None,
529        }
530    }
531
532    const fn runtime_boundary_detail(raw: u16) -> Option<DiagnosticDetail> {
533        match raw {
534            24 => Some(DiagnosticDetail::RuntimeBoundary {
535                boundary: RuntimeBoundaryCode::SqlSurfaceControllerRequired,
536            }),
537            25 => Some(DiagnosticDetail::RuntimeBoundary {
538                boundary: RuntimeBoundaryCode::SchemaSurfaceControllerRequired,
539            }),
540            26 => Some(DiagnosticDetail::RuntimeBoundary {
541                boundary: RuntimeBoundaryCode::SqlQueryNoConfiguredEntities,
542            }),
543            27 => Some(DiagnosticDetail::RuntimeBoundary {
544                boundary: RuntimeBoundaryCode::SqlQueryEntityNotConfigured,
545            }),
546            28 => Some(DiagnosticDetail::RuntimeBoundary {
547                boundary: RuntimeBoundaryCode::SqlDdlTargetRequired,
548            }),
549            29 => Some(DiagnosticDetail::RuntimeBoundary {
550                boundary: RuntimeBoundaryCode::SqlDdlEntityNotConfigured,
551            }),
552            30 => Some(DiagnosticDetail::RuntimeBoundary {
553                boundary: RuntimeBoundaryCode::QueryResponseRowsRequired,
554            }),
555            31 => Some(DiagnosticDetail::RuntimeBoundary {
556                boundary: RuntimeBoundaryCode::QueryResponseGroupedRowsRequired,
557            }),
558            32 => Some(DiagnosticDetail::RuntimeBoundary {
559                boundary: RuntimeBoundaryCode::MutationResultEntityRequired,
560            }),
561            33 => Some(DiagnosticDetail::RuntimeBoundary {
562                boundary: RuntimeBoundaryCode::MutationResultEntitiesRequired,
563            }),
564            34 => Some(DiagnosticDetail::RuntimeBoundary {
565                boundary: RuntimeBoundaryCode::MutationResultIdRequired,
566            }),
567            35 => Some(DiagnosticDetail::RuntimeBoundary {
568                boundary: RuntimeBoundaryCode::MutationResultIdsRequired,
569            }),
570            36 => Some(DiagnosticDetail::RuntimeBoundary {
571                boundary: RuntimeBoundaryCode::RowProjectionFieldNotConfigured,
572            }),
573            _ => None,
574        }
575    }
576
577    const fn sql_feature_detail(raw: u16) -> Option<DiagnosticDetail> {
578        let base = Self::SQL_FEATURE_AGGREGATE_FILTER_CLAUSE.raw();
579        if raw < base {
580            return None;
581        }
582
583        let offset = (raw - base) as usize;
584        if offset < Self::SQL_FEATURE_DETAILS.len() {
585            Some(DiagnosticDetail::UnsupportedSqlFeature {
586                feature: Self::SQL_FEATURE_DETAILS[offset],
587            })
588        } else {
589            None
590        }
591    }
592
593    const fn sql_surface_detail(raw: u16) -> Option<DiagnosticDetail> {
594        match raw {
595            100 => Some(DiagnosticDetail::SqlSurfaceMismatch {
596                mismatch: SqlSurfaceMismatchCode::QueryRejectsInsert,
597            }),
598            101 => Some(DiagnosticDetail::SqlSurfaceMismatch {
599                mismatch: SqlSurfaceMismatchCode::QueryRejectsUpdate,
600            }),
601            102 => Some(DiagnosticDetail::SqlSurfaceMismatch {
602                mismatch: SqlSurfaceMismatchCode::QueryRejectsDelete,
603            }),
604            103 => Some(DiagnosticDetail::SqlSurfaceMismatch {
605                mismatch: SqlSurfaceMismatchCode::UpdateRejectsSelect,
606            }),
607            104 => Some(DiagnosticDetail::SqlSurfaceMismatch {
608                mismatch: SqlSurfaceMismatchCode::UpdateRejectsExplain,
609            }),
610            105 => Some(DiagnosticDetail::SqlSurfaceMismatch {
611                mismatch: SqlSurfaceMismatchCode::UpdateRejectsDescribe,
612            }),
613            106 => Some(DiagnosticDetail::SqlSurfaceMismatch {
614                mismatch: SqlSurfaceMismatchCode::UpdateRejectsShowIndexes,
615            }),
616            107 => Some(DiagnosticDetail::SqlSurfaceMismatch {
617                mismatch: SqlSurfaceMismatchCode::UpdateRejectsShowColumns,
618            }),
619            108 => Some(DiagnosticDetail::SqlSurfaceMismatch {
620                mismatch: SqlSurfaceMismatchCode::UpdateRejectsShowEntities,
621            }),
622            109 => Some(DiagnosticDetail::SqlSurfaceMismatch {
623                mismatch: SqlSurfaceMismatchCode::UpdateRejectsShowStores,
624            }),
625            110 => Some(DiagnosticDetail::SqlSurfaceMismatch {
626                mismatch: SqlSurfaceMismatchCode::UpdateRejectsShowMemory,
627            }),
628            _ => None,
629        }
630    }
631
632    const fn schema_ddl_detail(raw: u16) -> Option<DiagnosticDetail> {
633        match raw {
634            111 => Some(DiagnosticDetail::SchemaDdlAdmission {
635                reason: SchemaDdlAdmissionCode::MissingExpectedSchemaVersion,
636            }),
637            112 => Some(DiagnosticDetail::SchemaDdlAdmission {
638                reason: SchemaDdlAdmissionCode::MissingNextSchemaVersion,
639            }),
640            113 => Some(DiagnosticDetail::SchemaDdlAdmission {
641                reason: SchemaDdlAdmissionCode::StaleExpectedSchemaVersion,
642            }),
643            114 => Some(DiagnosticDetail::SchemaDdlAdmission {
644                reason: SchemaDdlAdmissionCode::InvalidExpectedSchemaVersion,
645            }),
646            115 => Some(DiagnosticDetail::SchemaDdlAdmission {
647                reason: SchemaDdlAdmissionCode::InvalidNextSchemaVersion,
648            }),
649            116 => Some(DiagnosticDetail::SchemaDdlAdmission {
650                reason: SchemaDdlAdmissionCode::AcceptedSchemaChangeWithoutVersionBump,
651            }),
652            117 => Some(DiagnosticDetail::SchemaDdlAdmission {
653                reason: SchemaDdlAdmissionCode::EmptyVersionBump,
654            }),
655            118 => Some(DiagnosticDetail::SchemaDdlAdmission {
656                reason: SchemaDdlAdmissionCode::VersionGap,
657            }),
658            119 => Some(DiagnosticDetail::SchemaDdlAdmission {
659                reason: SchemaDdlAdmissionCode::VersionRollback,
660            }),
661            120 => Some(DiagnosticDetail::SchemaDdlAdmission {
662                reason: SchemaDdlAdmissionCode::FingerprintMethodMismatch,
663            }),
664            121 => Some(DiagnosticDetail::SchemaDdlAdmission {
665                reason: SchemaDdlAdmissionCode::UnsupportedTransitionClass,
666            }),
667            122 => Some(DiagnosticDetail::SchemaDdlAdmission {
668                reason: SchemaDdlAdmissionCode::PhysicalRunnerMissing,
669            }),
670            123 => Some(DiagnosticDetail::SchemaDdlAdmission {
671                reason: SchemaDdlAdmissionCode::ValidationFailed,
672            }),
673            124 => Some(DiagnosticDetail::SchemaDdlAdmission {
674                reason: SchemaDdlAdmissionCode::PublicationRaceLost,
675            }),
676            _ => None,
677        }
678    }
679}
680
681impl std::fmt::Debug for ErrorCode {
682    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
683        f.debug_tuple("ErrorCode").field(&self.0).finish()
684    }
685}
686
687///
688/// ErrorClass
689///
690/// Broad diagnostic class used for recovery decisions.
691///
692
693#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
694pub enum ErrorClass {
695    Query,
696    Corruption,
697    IncompatiblePersistedFormat,
698    NotFound,
699    Internal,
700    Conflict,
701    Unsupported,
702    InvariantViolation,
703}
704
705///
706/// ErrorOrigin
707///
708/// Subsystem that owns the diagnostic.
709///
710
711#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
712pub enum ErrorOrigin {
713    Cursor,
714    Executor,
715    Identity,
716    Index,
717    Interface,
718    Planner,
719    Query,
720    Recovery,
721    Response,
722    Runtime,
723    Serialize,
724    Store,
725}
726
727///
728/// QueryErrorKind
729///
730/// Public query error category.
731///
732
733#[repr(u16)]
734#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
735pub enum QueryErrorKind {
736    Validate,
737    Intent,
738    Plan,
739    AccessRequirement,
740    UnorderedPagination,
741    InvalidContinuationCursor,
742    NotFound,
743    NotUnique,
744}
745
746///
747/// RuntimeErrorKind
748///
749/// Public runtime error category.
750///
751
752#[repr(u16)]
753#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
754pub enum RuntimeErrorKind {
755    Corruption,
756    IncompatiblePersistedFormat,
757    InvariantViolation,
758    Conflict,
759    NotFound,
760    Unsupported,
761    Internal,
762}
763
764///
765/// RuntimeBoundaryCode
766///
767/// Compact public-runtime boundary identifier.
768///
769
770#[repr(u16)]
771#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
772pub enum RuntimeBoundaryCode {
773    SqlSurfaceControllerRequired,
774    SchemaSurfaceControllerRequired,
775    SqlQueryNoConfiguredEntities,
776    SqlQueryEntityNotConfigured,
777    SqlDdlTargetRequired,
778    SqlDdlEntityNotConfigured,
779    QueryResponseRowsRequired,
780    QueryResponseGroupedRowsRequired,
781    MutationResultEntityRequired,
782    MutationResultEntitiesRequired,
783    MutationResultIdRequired,
784    MutationResultIdsRequired,
785    RowProjectionFieldNotConfigured,
786}
787
788///
789/// SqlFeatureCode
790///
791/// Compact SQL feature identifier used by unsupported-feature diagnostics.
792///
793
794#[repr(u16)]
795#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
796pub enum SqlFeatureCode {
797    AggregateFilterClause,
798    AlterStatementBeyondAlterTable,
799    AlterTableAddColumnDuplicateDefault,
800    AlterTableAddColumnModifiers,
801    AlterTableAddStatementBeyondAddColumn,
802    AlterTableAlterColumnDropUnsupportedAction,
803    AlterTableAlterColumnModifiers,
804    AlterTableAlterColumnSetUnsupportedAction,
805    AlterTableAlterColumnUnsupportedAction,
806    AlterTableAlterStatementBeyondAlterColumn,
807    AlterTableDropColumnIfExistsSyntax,
808    AlterTableDropColumnModifiers,
809    AlterTableDropStatementBeyondDropColumn,
810    AlterTableRenameColumnMissingTo,
811    AlterTableRenameColumnModifiers,
812    AlterTableRenameStatementBeyondRenameColumn,
813    AlterTableUnsupportedOperation,
814    ColumnAlias,
815    CreateIndexIfNotExistsSyntax,
816    CreateIndexKeyOrderingModifiers,
817    CreateIndexModifiers,
818    CreateStatementBeyondCreateIndex,
819    DescribeModifier,
820    DdlSchemaVersionDuplicateExpectedClause,
821    DdlSchemaVersionDuplicateSetClause,
822    DropIndexModifiers,
823    DropIndexIfExistsSyntax,
824    DropStatementBeyondDropIndex,
825    ExpressionIndexUnsupportedFunction,
826    Having,
827    Insert,
828    Join,
829    LikePatternBeyondTrailingPrefix,
830    LowerFieldPredicateUnsupported,
831    MultiStatementSql,
832    NestedAggregateInput,
833    NestedProjectionFunctionInArithmetic,
834    OrderByUnsupportedForm,
835    Other,
836    ParameterBinding,
837    ParameterizedSchemaVersion,
838    PredicateStartsWithFirstArgument,
839    QuotedIdentifiers,
840    ReturningUnsupportedShape,
841    ScalarFunctionExpressionPosition,
842    ScaleTakingNumericFunctionExpressionPosition,
843    SearchedCaseGroupedOrderBy,
844    ShowColumnsModifiers,
845    ShowEntitiesModifiers,
846    ShowIndexesModifiers,
847    ShowMemoryModifiers,
848    ShowStoresModifiers,
849    ShowUnsupportedCommand,
850    SimpleCaseExpression,
851    StandaloneLiteralProjectionItem,
852    SupportedGroupedOrderByExpressionFamily,
853    SupportedOrderByExpressionFamily,
854    UnionIntersectExcept,
855    UnsupportedFunctionNamespace,
856    Update,
857    UpperFieldPredicateUnsupported,
858    WindowFunction,
859    With,
860}
861
862///
863/// SqlSurfaceMismatchCode
864///
865/// Compact SQL endpoint surface mismatch identifier.
866///
867
868#[repr(u16)]
869#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
870pub enum SqlSurfaceMismatchCode {
871    QueryRejectsInsert,
872    QueryRejectsUpdate,
873    QueryRejectsDelete,
874    UpdateRejectsSelect,
875    UpdateRejectsExplain,
876    UpdateRejectsDescribe,
877    UpdateRejectsShowIndexes,
878    UpdateRejectsShowColumns,
879    UpdateRejectsShowEntities,
880    UpdateRejectsShowStores,
881    UpdateRejectsShowMemory,
882}
883
884///
885/// SchemaDdlAdmissionCode
886///
887/// Compact SQL DDL admission rejection reason.
888///
889
890#[repr(u16)]
891#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
892pub enum SchemaDdlAdmissionCode {
893    MissingExpectedSchemaVersion,
894    MissingNextSchemaVersion,
895    StaleExpectedSchemaVersion,
896    InvalidExpectedSchemaVersion,
897    InvalidNextSchemaVersion,
898    AcceptedSchemaChangeWithoutVersionBump,
899    EmptyVersionBump,
900    VersionGap,
901    VersionRollback,
902    FingerprintMethodMismatch,
903    UnsupportedTransitionClass,
904    PhysicalRunnerMissing,
905    ValidationFailed,
906    PublicationRaceLost,
907}
908
909///
910/// DiagnosticDetail
911///
912/// Small structured diagnostic payload for callers and CLI rendering.
913///
914
915#[derive(Clone, Copy, Debug, Eq, PartialEq)]
916pub enum DiagnosticDetail {
917    QueryKind { kind: QueryErrorKind },
918    RuntimeKind { kind: RuntimeErrorKind },
919    RuntimeBoundary { boundary: RuntimeBoundaryCode },
920    SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
921    UnsupportedSqlFeature { feature: SqlFeatureCode },
922    SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
923}
924
925///
926/// Diagnostic
927///
928/// Compact public diagnostic payload.
929///
930
931#[derive(Clone, Debug, Eq, PartialEq)]
932pub struct Diagnostic {
933    code: DiagnosticCode,
934    origin: ErrorOrigin,
935    detail: Option<DiagnosticDetail>,
936}
937
938impl Diagnostic {
939    /// Build a compact diagnostic from a code and optional structured detail.
940    #[must_use]
941    pub const fn new(
942        code: DiagnosticCode,
943        origin: ErrorOrigin,
944        detail: Option<DiagnosticDetail>,
945    ) -> Self {
946        Self {
947            code,
948            origin,
949            detail,
950        }
951    }
952
953    /// Build a compact diagnostic using the code's default origin.
954    #[must_use]
955    pub const fn from_code(code: DiagnosticCode) -> Self {
956        Self::new(code, code.origin(), None)
957    }
958
959    /// Return the stable diagnostic code.
960    #[must_use]
961    pub const fn code(&self) -> DiagnosticCode {
962        self.code
963    }
964
965    /// Return the diagnostic class.
966    #[must_use]
967    pub const fn class(&self) -> ErrorClass {
968        self.code.class()
969    }
970
971    /// Return the subsystem origin.
972    #[must_use]
973    pub const fn origin(&self) -> ErrorOrigin {
974        self.origin
975    }
976
977    /// Return structured diagnostic detail, when available.
978    #[must_use]
979    pub const fn detail(&self) -> Option<&DiagnosticDetail> {
980        self.detail.as_ref()
981    }
982
983    /// Return the numeric public wire code for this diagnostic.
984    #[must_use]
985    pub const fn error_code(&self) -> ErrorCode {
986        ErrorCode::from_parts(self.code, self.detail)
987    }
988}
989
990#[cfg(test)]
991mod tests {
992    use super::{Diagnostic, DiagnosticCode, ErrorClass, ErrorCode, ErrorOrigin};
993
994    const ORDERED_ERROR_CODES: [ErrorCode; 124] = [
995        ErrorCode::QUERY_VALIDATE,
996        ErrorCode::QUERY_INTENT,
997        ErrorCode::QUERY_PLAN,
998        ErrorCode::QUERY_ACCESS_REQUIREMENT,
999        ErrorCode::QUERY_UNORDERED_PAGINATION,
1000        ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
1001        ErrorCode::QUERY_NOT_FOUND,
1002        ErrorCode::QUERY_NOT_UNIQUE,
1003        ErrorCode::QUERY_NUMERIC_OVERFLOW,
1004        ErrorCode::QUERY_NUMERIC_NOT_REPRESENTABLE,
1005        ErrorCode::QUERY_UNSUPPORTED_SQL_FEATURE,
1006        ErrorCode::QUERY_SQL_SURFACE_MISMATCH,
1007        ErrorCode::SCHEMA_DDL_ADMISSION,
1008        ErrorCode::STORE_NOT_FOUND,
1009        ErrorCode::STORE_CORRUPTION,
1010        ErrorCode::STORE_INVARIANT_VIOLATION,
1011        ErrorCode::RUNTIME_CORRUPTION,
1012        ErrorCode::RUNTIME_INCOMPATIBLE_PERSISTED_FORMAT,
1013        ErrorCode::RUNTIME_INVARIANT_VIOLATION,
1014        ErrorCode::RUNTIME_CONFLICT,
1015        ErrorCode::RUNTIME_NOT_FOUND,
1016        ErrorCode::RUNTIME_UNSUPPORTED,
1017        ErrorCode::RUNTIME_INTERNAL,
1018        ErrorCode::RUNTIME_BOUNDARY_SQL_SURFACE_CONTROLLER_REQUIRED,
1019        ErrorCode::RUNTIME_BOUNDARY_SCHEMA_SURFACE_CONTROLLER_REQUIRED,
1020        ErrorCode::RUNTIME_BOUNDARY_SQL_QUERY_NO_CONFIGURED_ENTITIES,
1021        ErrorCode::RUNTIME_BOUNDARY_SQL_QUERY_ENTITY_NOT_CONFIGURED,
1022        ErrorCode::RUNTIME_BOUNDARY_SQL_DDL_TARGET_REQUIRED,
1023        ErrorCode::RUNTIME_BOUNDARY_SQL_DDL_ENTITY_NOT_CONFIGURED,
1024        ErrorCode::RUNTIME_BOUNDARY_QUERY_RESPONSE_ROWS_REQUIRED,
1025        ErrorCode::RUNTIME_BOUNDARY_QUERY_RESPONSE_GROUPED_ROWS_REQUIRED,
1026        ErrorCode::RUNTIME_BOUNDARY_MUTATION_RESULT_ENTITY_REQUIRED,
1027        ErrorCode::RUNTIME_BOUNDARY_MUTATION_RESULT_ENTITIES_REQUIRED,
1028        ErrorCode::RUNTIME_BOUNDARY_MUTATION_RESULT_ID_REQUIRED,
1029        ErrorCode::RUNTIME_BOUNDARY_MUTATION_RESULT_IDS_REQUIRED,
1030        ErrorCode::RUNTIME_BOUNDARY_ROW_PROJECTION_FIELD_NOT_CONFIGURED,
1031        ErrorCode::SQL_FEATURE_AGGREGATE_FILTER_CLAUSE,
1032        ErrorCode::SQL_FEATURE_ALTER_STATEMENT_BEYOND_ALTER_TABLE,
1033        ErrorCode::SQL_FEATURE_ALTER_TABLE_ADD_COLUMN_DUPLICATE_DEFAULT,
1034        ErrorCode::SQL_FEATURE_ALTER_TABLE_ADD_COLUMN_MODIFIERS,
1035        ErrorCode::SQL_FEATURE_ALTER_TABLE_ADD_STATEMENT_BEYOND_ADD_COLUMN,
1036        ErrorCode::SQL_FEATURE_ALTER_TABLE_ALTER_COLUMN_DROP_UNSUPPORTED_ACTION,
1037        ErrorCode::SQL_FEATURE_ALTER_TABLE_ALTER_COLUMN_MODIFIERS,
1038        ErrorCode::SQL_FEATURE_ALTER_TABLE_ALTER_COLUMN_SET_UNSUPPORTED_ACTION,
1039        ErrorCode::SQL_FEATURE_ALTER_TABLE_ALTER_COLUMN_UNSUPPORTED_ACTION,
1040        ErrorCode::SQL_FEATURE_ALTER_TABLE_ALTER_STATEMENT_BEYOND_ALTER_COLUMN,
1041        ErrorCode::SQL_FEATURE_ALTER_TABLE_DROP_COLUMN_IF_EXISTS_SYNTAX,
1042        ErrorCode::SQL_FEATURE_ALTER_TABLE_DROP_COLUMN_MODIFIERS,
1043        ErrorCode::SQL_FEATURE_ALTER_TABLE_DROP_STATEMENT_BEYOND_DROP_COLUMN,
1044        ErrorCode::SQL_FEATURE_ALTER_TABLE_RENAME_COLUMN_MISSING_TO,
1045        ErrorCode::SQL_FEATURE_ALTER_TABLE_RENAME_COLUMN_MODIFIERS,
1046        ErrorCode::SQL_FEATURE_ALTER_TABLE_RENAME_STATEMENT_BEYOND_RENAME_COLUMN,
1047        ErrorCode::SQL_FEATURE_ALTER_TABLE_UNSUPPORTED_OPERATION,
1048        ErrorCode::SQL_FEATURE_COLUMN_ALIAS,
1049        ErrorCode::SQL_FEATURE_CREATE_INDEX_IF_NOT_EXISTS_SYNTAX,
1050        ErrorCode::SQL_FEATURE_CREATE_INDEX_KEY_ORDERING_MODIFIERS,
1051        ErrorCode::SQL_FEATURE_CREATE_INDEX_MODIFIERS,
1052        ErrorCode::SQL_FEATURE_CREATE_STATEMENT_BEYOND_CREATE_INDEX,
1053        ErrorCode::SQL_FEATURE_DESCRIBE_MODIFIER,
1054        ErrorCode::SQL_FEATURE_DDL_SCHEMA_VERSION_DUPLICATE_EXPECTED_CLAUSE,
1055        ErrorCode::SQL_FEATURE_DDL_SCHEMA_VERSION_DUPLICATE_SET_CLAUSE,
1056        ErrorCode::SQL_FEATURE_DROP_INDEX_MODIFIERS,
1057        ErrorCode::SQL_FEATURE_DROP_INDEX_IF_EXISTS_SYNTAX,
1058        ErrorCode::SQL_FEATURE_DROP_STATEMENT_BEYOND_DROP_INDEX,
1059        ErrorCode::SQL_FEATURE_EXPRESSION_INDEX_UNSUPPORTED_FUNCTION,
1060        ErrorCode::SQL_FEATURE_HAVING,
1061        ErrorCode::SQL_FEATURE_INSERT,
1062        ErrorCode::SQL_FEATURE_JOIN,
1063        ErrorCode::SQL_FEATURE_LIKE_PATTERN_BEYOND_TRAILING_PREFIX,
1064        ErrorCode::SQL_FEATURE_LOWER_FIELD_PREDICATE_UNSUPPORTED,
1065        ErrorCode::SQL_FEATURE_MULTI_STATEMENT_SQL,
1066        ErrorCode::SQL_FEATURE_NESTED_AGGREGATE_INPUT,
1067        ErrorCode::SQL_FEATURE_NESTED_PROJECTION_FUNCTION_IN_ARITHMETIC,
1068        ErrorCode::SQL_FEATURE_ORDER_BY_UNSUPPORTED_FORM,
1069        ErrorCode::SQL_FEATURE_OTHER,
1070        ErrorCode::SQL_FEATURE_PARAMETER_BINDING,
1071        ErrorCode::SQL_FEATURE_PARAMETERIZED_SCHEMA_VERSION,
1072        ErrorCode::SQL_FEATURE_PREDICATE_STARTS_WITH_FIRST_ARGUMENT,
1073        ErrorCode::SQL_FEATURE_QUOTED_IDENTIFIERS,
1074        ErrorCode::SQL_FEATURE_RETURNING_UNSUPPORTED_SHAPE,
1075        ErrorCode::SQL_FEATURE_SCALAR_FUNCTION_EXPRESSION_POSITION,
1076        ErrorCode::SQL_FEATURE_SCALE_TAKING_NUMERIC_FUNCTION_EXPRESSION_POSITION,
1077        ErrorCode::SQL_FEATURE_SEARCHED_CASE_GROUPED_ORDER_BY,
1078        ErrorCode::SQL_FEATURE_SHOW_COLUMNS_MODIFIERS,
1079        ErrorCode::SQL_FEATURE_SHOW_ENTITIES_MODIFIERS,
1080        ErrorCode::SQL_FEATURE_SHOW_INDEXES_MODIFIERS,
1081        ErrorCode::SQL_FEATURE_SHOW_MEMORY_MODIFIERS,
1082        ErrorCode::SQL_FEATURE_SHOW_STORES_MODIFIERS,
1083        ErrorCode::SQL_FEATURE_SHOW_UNSUPPORTED_COMMAND,
1084        ErrorCode::SQL_FEATURE_SIMPLE_CASE_EXPRESSION,
1085        ErrorCode::SQL_FEATURE_STANDALONE_LITERAL_PROJECTION_ITEM,
1086        ErrorCode::SQL_FEATURE_SUPPORTED_GROUPED_ORDER_BY_EXPRESSION_FAMILY,
1087        ErrorCode::SQL_FEATURE_SUPPORTED_ORDER_BY_EXPRESSION_FAMILY,
1088        ErrorCode::SQL_FEATURE_UNION_INTERSECT_EXCEPT,
1089        ErrorCode::SQL_FEATURE_UNSUPPORTED_FUNCTION_NAMESPACE,
1090        ErrorCode::SQL_FEATURE_UPDATE,
1091        ErrorCode::SQL_FEATURE_UPPER_FIELD_PREDICATE_UNSUPPORTED,
1092        ErrorCode::SQL_FEATURE_WINDOW_FUNCTION,
1093        ErrorCode::SQL_FEATURE_WITH,
1094        ErrorCode::SQL_SURFACE_QUERY_REJECTS_INSERT,
1095        ErrorCode::SQL_SURFACE_QUERY_REJECTS_UPDATE,
1096        ErrorCode::SQL_SURFACE_QUERY_REJECTS_DELETE,
1097        ErrorCode::SQL_SURFACE_UPDATE_REJECTS_SELECT,
1098        ErrorCode::SQL_SURFACE_UPDATE_REJECTS_EXPLAIN,
1099        ErrorCode::SQL_SURFACE_UPDATE_REJECTS_DESCRIBE,
1100        ErrorCode::SQL_SURFACE_UPDATE_REJECTS_SHOW_INDEXES,
1101        ErrorCode::SQL_SURFACE_UPDATE_REJECTS_SHOW_COLUMNS,
1102        ErrorCode::SQL_SURFACE_UPDATE_REJECTS_SHOW_ENTITIES,
1103        ErrorCode::SQL_SURFACE_UPDATE_REJECTS_SHOW_STORES,
1104        ErrorCode::SQL_SURFACE_UPDATE_REJECTS_SHOW_MEMORY,
1105        ErrorCode::SCHEMA_DDL_MISSING_EXPECTED_SCHEMA_VERSION,
1106        ErrorCode::SCHEMA_DDL_MISSING_NEXT_SCHEMA_VERSION,
1107        ErrorCode::SCHEMA_DDL_STALE_EXPECTED_SCHEMA_VERSION,
1108        ErrorCode::SCHEMA_DDL_INVALID_EXPECTED_SCHEMA_VERSION,
1109        ErrorCode::SCHEMA_DDL_INVALID_NEXT_SCHEMA_VERSION,
1110        ErrorCode::SCHEMA_DDL_ACCEPTED_SCHEMA_CHANGE_WITHOUT_VERSION_BUMP,
1111        ErrorCode::SCHEMA_DDL_EMPTY_VERSION_BUMP,
1112        ErrorCode::SCHEMA_DDL_VERSION_GAP,
1113        ErrorCode::SCHEMA_DDL_VERSION_ROLLBACK,
1114        ErrorCode::SCHEMA_DDL_FINGERPRINT_METHOD_MISMATCH,
1115        ErrorCode::SCHEMA_DDL_UNSUPPORTED_TRANSITION_CLASS,
1116        ErrorCode::SCHEMA_DDL_PHYSICAL_RUNNER_MISSING,
1117        ErrorCode::SCHEMA_DDL_VALIDATION_FAILED,
1118        ErrorCode::SCHEMA_DDL_PUBLICATION_RACE_LOST,
1119    ];
1120
1121    #[test]
1122    fn diagnostic_from_code_uses_default_origin() {
1123        let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
1124
1125        assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
1126        assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1127    }
1128
1129    #[test]
1130    fn diagnostic_code_reports_broad_class() {
1131        assert_eq!(
1132            DiagnosticCode::QueryUnsupportedSqlFeature.class(),
1133            ErrorClass::Unsupported
1134        );
1135        assert_eq!(
1136            DiagnosticCode::QuerySqlSurfaceMismatch.class(),
1137            ErrorClass::Unsupported
1138        );
1139        assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
1140        assert_eq!(
1141            DiagnosticCode::StoreCorruption.class(),
1142            ErrorClass::Corruption
1143        );
1144    }
1145
1146    #[test]
1147    fn public_error_codes_are_sequential() {
1148        for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
1149            let expected = u16::try_from(index + 1).expect("test error-code index fits u16");
1150            assert_eq!(code.raw(), expected);
1151        }
1152    }
1153}