1use std::fmt;
8
9pub const MAX_PUBLIC_DIAGNOSTIC_FACTS: usize = 80;
11
12macro_rules! define_fact_tag_registry {
13 ($($name:ident = $raw:literal;)+) => {
14 #[derive(Clone, Copy, Eq, Hash, PartialEq)]
16 pub enum DiagnosticFactTag {
17 $(
18 #[doc = concat!("Public fact tag ", stringify!($raw), ".")]
19 $name,
20 )+
21 }
22
23 impl DiagnosticFactTag {
24 #[must_use]
26 pub const fn raw(self) -> u8 {
27 match self {
28 $(Self::$name => $raw,)+
29 }
30 }
31
32 #[must_use]
34 pub const fn known(raw: u8) -> Option<Self> {
35 match raw {
36 $($raw => Some(Self::$name),)+
37 _ => None,
38 }
39 }
40 }
41
42 #[cfg(test)]
43 const ORDERED_FACT_TAGS: &[DiagnosticFactTag] = &[
44 $(DiagnosticFactTag::$name,)+
45 ];
46 };
47}
48
49define_fact_tag_registry! {
52 AcceptedSchemaFingerprintMethod = 1;
53 AcceptedSchemaFingerprintHigh = 2;
54 AcceptedSchemaFingerprintLow = 3;
55 ExpectedFingerprintPrefix = 4;
56 ActualFingerprintPrefix = 5;
57 EntityTag = 6;
58 ExpectedEntityTag = 7;
59 ActualEntityTag = 8;
60 ConstraintId = 9;
61 FieldId = 10;
62 IndexId = 11;
63 RelationId = 12;
64 MutationOperation = 13;
65 RowOperation = 14;
66 BatchPosition = 15;
67 FirstBatchPosition = 16;
68 DuplicateBatchPosition = 17;
69 ClauseIndex = 18;
70 TermIndex = 19;
71 FirstTermIndex = 20;
72 DuplicateTermIndex = 21;
73 ProjectionIndex = 22;
74 GroupIndex = 23;
75 AggregateIndex = 24;
76 ArgumentIndex = 25;
77 BranchIndex = 26;
78 ComponentIndex = 27;
79 ParameterIndex = 28;
80 SourceSpanStart = 29;
81 SourceSpanEnd = 30;
82 Expected = 31;
83 Actual = 32;
84 Minimum = 33;
85 Maximum = 34;
86 Limit = 35;
87 ExpectedCount = 36;
88 ActualCount = 37;
89 ExpectedRevision = 38;
90 ActualRevision = 39;
91 CurrentRevision = 40;
92 RequestedRevision = 41;
93 ExpectedVersion = 42;
94 ActualVersion = 43;
95 CurrentVersion = 44;
96 RequestedVersion = 45;
97 ExpectedOffset = 46;
98 ActualOffset = 47;
99 ExpectedArity = 48;
100 ActualArity = 49;
101 ExpectedLength = 50;
102 ActualLength = 51;
103 ExpectedSlotCount = 52;
104 ActualSlotCount = 53;
105 RowLayout = 54;
106 HistoryFloor = 55;
107 CurrentLayout = 56;
108 PhysicalSlot = 57;
109 PhysicalGeneration = 58;
110 ExpectedMemoryId = 59;
111 ActualMemoryId = 60;
112 ConstraintKind = 61;
113 ConstraintContext = 62;
114 FieldKind = 63;
115 ValueKind = 64;
116 TypeFamily = 65;
117 FunctionKind = 66;
118 OperatorKind = 67;
119 AggregateKind = 68;
120 KeyNamespaceKind = 69;
121 ComponentKind = 70;
122 MismatchKind = 71;
123 DecodeReason = 72;
124 BudgetResource = 73;
125 MigrationPhase = 74;
126 DatabaseControlRecordKind = 75;
127 StateKind = 76;
128 PayloadComponent = 77;
129 ExpectedSignaturePrefix = 78;
130 ActualSignaturePrefix = 79;
131 FindingPosition = 80;
132 RootField = 81;
133 RecordMember = 82;
134 TupleElement = 83;
135 Newtype = 84;
136 EnumVariant = 85;
137 ListElement = 86;
138 SetElement = 87;
139 MapEntryKey = 88;
140 MapEntryValue = 89;
141}
142
143#[derive(Clone, Copy, Eq, Hash, PartialEq)]
148pub enum DiagnosticDecodeReason {
149 CursorEmpty,
150 CursorTooLong,
151 CursorOddLength,
152 CursorInvalidHex,
153 CursorGroupedDirectionMismatch,
154 CursorTokenEncode,
155 CursorTokenDecode,
156 RecoveryMarkerMagic,
157 RecoveryMarkerChecksum,
158 RecoveryMarkerState,
159}
160
161#[derive(Clone, Copy, Eq, Hash, PartialEq)]
163pub enum DiagnosticMutationOperation {
164 Insert,
165 Replace,
166 Update,
167 Delete,
168}
169
170macro_rules! define_numeric_fact_value_registry {
171 (
172 $(#[$enum_meta:meta])*
173 pub enum $name:ident {
174 $($variant:ident = $raw:literal;)+
175 }
176 ) => {
177 $(#[$enum_meta])*
178 #[derive(Clone, Copy, Eq, Hash, PartialEq)]
179 pub enum $name {
180 $(
181 #[doc = concat!("Compact diagnostic value ", stringify!($raw), ".")]
182 $variant,
183 )+
184 }
185
186 impl $name {
187 #[must_use]
189 pub const fn raw(self) -> u64 {
190 match self {
191 $(Self::$variant => $raw,)+
192 }
193 }
194
195 #[must_use]
197 pub const fn known(raw: u64) -> Option<Self> {
198 match raw {
199 $($raw => Some(Self::$variant),)+
200 _ => None,
201 }
202 }
203 }
204
205 impl fmt::Debug for $name {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 write!(f, "{}", self.raw())
208 }
209 }
210 };
211}
212
213define_numeric_fact_value_registry! {
214 pub enum DiagnosticConstraintKind {
216 Check = 1;
217 NotNull = 2;
218 Relation = 3;
219 TargetedRule = 4;
220 Unique = 5;
221 }
222}
223
224define_numeric_fact_value_registry! {
225 pub enum DiagnosticConstraintContext {
227 Integrity = 1;
228 MigrationValidation = 2;
229 WriteAdmission = 3;
230 }
231}
232
233define_numeric_fact_value_registry! {
234 pub enum DiagnosticComponentKind {
236 CommitDataKey = 1;
237 IndexKey = 2;
238 IndexKeyComponent = 3;
239 RelationTargetPrimaryKey = 4;
240 }
241}
242
243define_numeric_fact_value_registry! {
244 pub enum DiagnosticTypeFamily {
246 Blob = 1;
247 Bool = 2;
248 Collection = 3;
249 Null = 4;
250 Numeric = 5;
251 Opaque = 6;
252 Structured = 7;
253 Text = 8;
254 Unknown = 9;
255 }
256}
257
258define_numeric_fact_value_registry! {
259 pub enum DiagnosticFunctionKind {
261 Abs = 1;
262 Cbrt = 2;
263 Ceiling = 3;
264 Coalesce = 4;
265 CollectionContains = 5;
266 Contains = 6;
267 EndsWith = 7;
268 Exp = 8;
269 Floor = 9;
270 IsEmpty = 10;
271 IsMissing = 11;
272 IsNotEmpty = 12;
273 IsNotNull = 13;
274 IsNull = 14;
275 Left = 15;
276 Length = 16;
277 Ln = 17;
278 Log = 18;
279 Log2 = 19;
280 Log10 = 20;
281 Lower = 21;
282 Ltrim = 22;
283 Mod = 23;
284 NullIf = 24;
285 OctetLength = 25;
286 Position = 26;
287 Power = 27;
288 Replace = 28;
289 Right = 29;
290 Round = 30;
291 Rtrim = 31;
292 Sign = 32;
293 Sqrt = 33;
294 StartsWith = 34;
295 Substring = 35;
296 Trim = 36;
297 Trunc = 37;
298 Upper = 38;
299 InList = 39;
300 }
301}
302
303define_numeric_fact_value_registry! {
304 pub enum DiagnosticOperatorKind {
306 Not = 1;
307 Add = 2;
308 And = 3;
309 Div = 4;
310 Eq = 5;
311 Gt = 6;
312 Gte = 7;
313 Lt = 8;
314 Lte = 9;
315 Mul = 10;
316 Ne = 11;
317 Or = 12;
318 Sub = 13;
319 In = 14;
320 NotIn = 15;
321 Contains = 16;
322 StartsWith = 17;
323 EndsWith = 18;
324 }
325}
326
327define_numeric_fact_value_registry! {
328 pub enum DiagnosticAggregateKind {
330 Count = 1;
331 Sum = 2;
332 Avg = 3;
333 Exists = 4;
334 Min = 5;
335 Max = 6;
336 First = 7;
337 Last = 8;
338 }
339}
340
341impl DiagnosticDecodeReason {
342 #[must_use]
344 pub const fn raw(self) -> u64 {
345 match self {
346 Self::CursorEmpty => 1,
347 Self::CursorTooLong => 2,
348 Self::CursorOddLength => 3,
349 Self::CursorInvalidHex => 4,
350 Self::CursorGroupedDirectionMismatch => 5,
351 Self::CursorTokenEncode => 6,
352 Self::CursorTokenDecode => 7,
353 Self::RecoveryMarkerMagic => 8,
354 Self::RecoveryMarkerChecksum => 9,
355 Self::RecoveryMarkerState => 10,
356 }
357 }
358
359 #[must_use]
361 pub const fn known(raw: u64) -> Option<Self> {
362 match raw {
363 1 => Some(Self::CursorEmpty),
364 2 => Some(Self::CursorTooLong),
365 3 => Some(Self::CursorOddLength),
366 4 => Some(Self::CursorInvalidHex),
367 5 => Some(Self::CursorGroupedDirectionMismatch),
368 6 => Some(Self::CursorTokenEncode),
369 7 => Some(Self::CursorTokenDecode),
370 8 => Some(Self::RecoveryMarkerMagic),
371 9 => Some(Self::RecoveryMarkerChecksum),
372 10 => Some(Self::RecoveryMarkerState),
373 _ => None,
374 }
375 }
376}
377
378impl DiagnosticMutationOperation {
379 #[must_use]
381 pub const fn raw(self) -> u64 {
382 match self {
383 Self::Insert => 1,
384 Self::Replace => 2,
385 Self::Update => 3,
386 Self::Delete => 4,
387 }
388 }
389
390 #[must_use]
392 pub const fn known(raw: u64) -> Option<Self> {
393 match raw {
394 1 => Some(Self::Insert),
395 2 => Some(Self::Replace),
396 3 => Some(Self::Update),
397 4 => Some(Self::Delete),
398 _ => None,
399 }
400 }
401}
402
403impl fmt::Debug for DiagnosticFactTag {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 write!(f, "{}", self.raw())
406 }
407}
408
409impl fmt::Debug for DiagnosticDecodeReason {
410 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
411 write!(f, "{}", self.raw())
412 }
413}
414
415impl fmt::Debug for DiagnosticMutationOperation {
416 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417 write!(f, "{}", self.raw())
418 }
419}
420
421#[must_use]
423pub const fn pack_u32_pair(high: u32, low: u32) -> u64 {
424 (high as u64) << 32 | low as u64
425}
426
427#[must_use]
429pub const fn unpack_u32_pair(value: u64) -> (u32, u32) {
430 let bytes = value.to_be_bytes();
431 (
432 u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
433 u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
434 )
435}
436
437#[cfg(test)]
438mod tests {
439 use super::{
440 DiagnosticAggregateKind, DiagnosticComponentKind, DiagnosticConstraintContext,
441 DiagnosticConstraintKind, DiagnosticDecodeReason, DiagnosticFactTag,
442 DiagnosticFunctionKind, DiagnosticMutationOperation, DiagnosticOperatorKind,
443 DiagnosticTypeFamily, ORDERED_FACT_TAGS, pack_u32_pair, unpack_u32_pair,
444 };
445
446 #[test]
447 fn fact_tag_registry_is_fixed_unique_and_contiguous() {
448 for (index, tag) in ORDERED_FACT_TAGS.iter().copied().enumerate() {
449 let expected = u8::try_from(index + 1).expect("fact-tag index fits u8");
450 assert_eq!(tag.raw(), expected);
451 assert_eq!(DiagnosticFactTag::known(expected), Some(tag));
452 }
453
454 assert_eq!(DiagnosticFactTag::known(0), None);
455 assert_eq!(DiagnosticFactTag::known(90), None);
456 assert_eq!(DiagnosticFactTag::known(u8::MAX), None);
457 }
458
459 #[test]
460 fn accepted_identity_pair_packing_is_exact() {
461 for pair in [
462 (0, 0),
463 (1, 2),
464 (u32::MAX, 0),
465 (0, u32::MAX),
466 (u32::MAX, u32::MAX),
467 ] {
468 assert_eq!(unpack_u32_pair(pack_u32_pair(pair.0, pair.1)), pair);
469 }
470 }
471
472 #[test]
473 fn constraint_fact_value_registries_are_fixed() {
474 assert_eq!(DiagnosticConstraintKind::Check.raw(), 1);
475 assert_eq!(DiagnosticConstraintKind::NotNull.raw(), 2);
476 assert_eq!(DiagnosticConstraintKind::Relation.raw(), 3);
477 assert_eq!(DiagnosticConstraintKind::TargetedRule.raw(), 4);
478 assert_eq!(DiagnosticConstraintKind::Unique.raw(), 5);
479 assert_eq!(DiagnosticConstraintKind::known(0), None);
480 assert_eq!(DiagnosticConstraintKind::known(6), None);
481
482 assert_eq!(DiagnosticConstraintContext::Integrity.raw(), 1);
483 assert_eq!(DiagnosticConstraintContext::MigrationValidation.raw(), 2);
484 assert_eq!(DiagnosticConstraintContext::WriteAdmission.raw(), 3);
485 assert_eq!(DiagnosticConstraintContext::known(0), None);
486 assert_eq!(DiagnosticConstraintContext::known(4), None);
487 }
488
489 #[test]
490 fn component_kind_registry_is_fixed_and_numeric() {
491 let kinds = [
492 DiagnosticComponentKind::CommitDataKey,
493 DiagnosticComponentKind::IndexKey,
494 DiagnosticComponentKind::IndexKeyComponent,
495 DiagnosticComponentKind::RelationTargetPrimaryKey,
496 ];
497
498 for (index, kind) in kinds.iter().copied().enumerate() {
499 let expected = (index + 1) as u64;
500 assert_eq!(kind.raw(), expected);
501 assert_eq!(DiagnosticComponentKind::known(expected), Some(kind));
502 assert_eq!(format!("{kind:?}"), expected.to_string());
503 }
504 assert_eq!(DiagnosticComponentKind::known(0), None);
505 assert_eq!(DiagnosticComponentKind::known(5), None);
506 }
507
508 #[test]
509 fn decode_reason_registry_is_fixed_and_numeric() {
510 let reasons = [
511 DiagnosticDecodeReason::CursorEmpty,
512 DiagnosticDecodeReason::CursorTooLong,
513 DiagnosticDecodeReason::CursorOddLength,
514 DiagnosticDecodeReason::CursorInvalidHex,
515 DiagnosticDecodeReason::CursorGroupedDirectionMismatch,
516 DiagnosticDecodeReason::CursorTokenEncode,
517 DiagnosticDecodeReason::CursorTokenDecode,
518 DiagnosticDecodeReason::RecoveryMarkerMagic,
519 DiagnosticDecodeReason::RecoveryMarkerChecksum,
520 DiagnosticDecodeReason::RecoveryMarkerState,
521 ];
522
523 for (index, reason) in reasons.iter().copied().enumerate() {
524 let expected = (index + 1) as u64;
525 assert_eq!(reason.raw(), expected);
526 assert_eq!(DiagnosticDecodeReason::known(expected), Some(reason));
527 assert_eq!(format!("{reason:?}"), expected.to_string());
528 }
529
530 assert_eq!(DiagnosticDecodeReason::known(0), None);
531 assert_eq!(DiagnosticDecodeReason::known(11), None);
532 }
533
534 #[test]
535 fn mutation_operation_registry_is_fixed_and_numeric() {
536 let operations = [
537 DiagnosticMutationOperation::Insert,
538 DiagnosticMutationOperation::Replace,
539 DiagnosticMutationOperation::Update,
540 DiagnosticMutationOperation::Delete,
541 ];
542
543 for (index, operation) in operations.iter().copied().enumerate() {
544 let expected = (index + 1) as u64;
545 assert_eq!(operation.raw(), expected);
546 assert_eq!(
547 DiagnosticMutationOperation::known(expected),
548 Some(operation)
549 );
550 assert_eq!(format!("{operation:?}"), expected.to_string());
551 }
552
553 assert_eq!(DiagnosticMutationOperation::known(0), None);
554 assert_eq!(DiagnosticMutationOperation::known(5), None);
555 }
556
557 #[test]
558 fn query_kind_registries_are_fixed_contiguous_and_numeric() {
559 for raw in 1..=9 {
560 let value = DiagnosticTypeFamily::known(raw).expect("type family should be known");
561 assert_eq!(value.raw(), raw);
562 assert_eq!(format!("{value:?}"), raw.to_string());
563 }
564 assert_eq!(DiagnosticTypeFamily::known(0), None);
565 assert_eq!(DiagnosticTypeFamily::known(10), None);
566
567 for raw in 1..=39 {
568 let value = DiagnosticFunctionKind::known(raw).expect("function kind should be known");
569 assert_eq!(value.raw(), raw);
570 assert_eq!(format!("{value:?}"), raw.to_string());
571 }
572 assert_eq!(DiagnosticFunctionKind::known(0), None);
573 assert_eq!(DiagnosticFunctionKind::known(40), None);
574
575 for raw in 1..=18 {
576 let value = DiagnosticOperatorKind::known(raw).expect("operator kind should be known");
577 assert_eq!(value.raw(), raw);
578 assert_eq!(format!("{value:?}"), raw.to_string());
579 }
580 assert_eq!(DiagnosticOperatorKind::known(0), None);
581 assert_eq!(DiagnosticOperatorKind::known(19), None);
582
583 for raw in 1..=8 {
584 let value =
585 DiagnosticAggregateKind::known(raw).expect("aggregate kind should be known");
586 assert_eq!(value.raw(), raw);
587 assert_eq!(format!("{value:?}"), raw.to_string());
588 }
589 assert_eq!(DiagnosticAggregateKind::known(0), None);
590 assert_eq!(DiagnosticAggregateKind::known(9), None);
591 }
592}