1use crate::{DiagnosticCode, InvalidDiagnosticCode, Severity};
4
5#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
7#[repr(u32)]
8#[non_exhaustive]
9#[allow(
10 missing_docs,
11 reason = "each variant is documented by its public catalogue definition"
12)]
13pub enum RuleId {
14 ComposeModelInvalid = 1_000_001,
15 ComposeProfileRequired = 1_000_002,
16 ComposeProfileMismatch = 1_000_003,
17 ComposeIntentUnsupported = 1_000_004,
18 ComposeValueInvalid = 1_000_005,
19 ComposeTargetInvalid = 1_000_006,
20 ComposeOutputUnsupported = 1_000_007,
21 ComposeGenerationFailed = 1_000_008,
22 ComposeCompatibilityConstraint = 1_000_009,
23 ComposeUnsetVariable = 1_000_101,
24 ComposeRequiredVariable = 1_000_102,
25 ComposeInterpolationInvalid = 1_000_103,
26 ComposeInterpolationNestingLimit = 1_000_104,
27 ComposeUnresolvedVariable = 1_000_105,
28 ComposeNativeError = 1_000_197,
29 ComposeNativeWarning = 1_000_198,
30 ComposeNativeNote = 1_000_199,
31 DockerDataInvalid = 2_000_001,
32 DockerConfigurationUnsupported = 2_000_002,
33 DockerVersionUnsupported = 2_000_003,
34 DockerRelationshipMissing = 2_000_004,
35 OrchestrationFailed = 3_001_000,
36 InputReadFailed = 3_001_001,
37 ComposeProjectRootInvalid = 3_001_002,
38 PodmanTargetSelectionInvalid = 3_001_003,
39 InterpolationInputInvalid = 3_001_004,
40 ComposeLoadFailed = 3_001_005,
41 ConversionFailed = 3_001_006,
42 QuadletParseFailed = 3_001_007,
43 OutputDirectoryNotEmpty = 3_002_001,
44 OutputPathInvalid = 3_002_002,
45 OutputWriteFailed = 3_002_003,
46 ReportWriteFailed = 3_003_001,
47 SupportBundleWriteFailed = 3_003_002,
48 PodmanDataInvalid = 4_000_001,
49 PodmanConfigurationUnsupported = 4_000_002,
50 PodmanVersionUnsupported = 4_000_003,
51 PodmanRelationshipMissing = 4_000_004,
52 QuadletTargetInvalid = 5_000_001,
53 QuadletOpenEndedTarget = 5_000_002,
54 QuadletOutputUnsupported = 5_000_003,
55 QuadletValueInvalid = 5_000_004,
56 QuadletGenerationFailed = 5_000_005,
57 QuadletCapabilityUnavailable = 5_000_006,
58 QuadletGroupingApproximation = 5_000_007,
59 QuadletDependencyUnsupported = 5_000_008,
60 QuadletRestartApproximation = 5_000_009,
61 QuadletEnvironmentFileApproximation = 5_000_010,
62 QuadletGroupingInvalid = 5_000_011,
63 QuadletDependencyInvalid = 5_000_012,
64 QuadletCapabilityDeprecated = 5_000_013,
65 QuadletUnresolvedVariable = 5_000_014,
66 QuadletSourceInvalid = 5_001_001,
67 QuadletModelInvalid = 5_001_002,
68 QuadletInputUnsupported = 5_001_003,
69 QuadletInputApproximation = 5_001_004,
70 QuadletNativeSyntax = 5_001_101,
71 QuadletNativeModel = 5_001_102,
72 QuadletNativeDocumentSet = 5_001_103,
73 QuadletNativeFailure = 5_001_104,
74 RuntimeReconstructionUncertain = 6_000_001,
75 RuntimeOverrideInferred = 6_000_002,
76 RuntimeComparisonIncomplete = 6_000_003,
77 RuntimeOwnershipUncertain = 6_000_004,
78 RuntimePodRelationshipIncomplete = 6_000_005,
79 RuntimeImageMissing = 6_000_006,
80 RuntimeModelInvalid = 6_000_007,
81 RuntimeGroupConflict = 6_000_008,
82 RuntimeLifecycleResolved = 6_000_009,
83 RuntimeManagedMetadata = 6_000_010,
84}
85
86#[derive(Clone, Copy, Debug, Eq, PartialEq)]
88pub struct DiagnosticRule {
89 id: RuleId,
90 code: &'static str,
91 name: &'static str,
92 default_severity: Severity,
93 description: &'static str,
94 help: &'static str,
95}
96
97impl DiagnosticRule {
98 #[must_use]
100 pub const fn id(self) -> RuleId {
101 self.id
102 }
103
104 #[must_use]
106 pub const fn code(self) -> &'static str {
107 self.code
108 }
109
110 #[must_use]
112 pub const fn name(self) -> &'static str {
113 self.name
114 }
115
116 #[must_use]
118 pub const fn default_severity(self) -> Severity {
119 self.default_severity
120 }
121
122 #[must_use]
124 pub fn owner(self) -> &'static str {
125 match self.code.as_bytes().get(..3) {
126 Some(b"BFC") => "Compose adapter",
127 Some(b"BFD") => "Docker adapter",
128 Some(b"BFO") => "BoxFerry orchestration",
129 Some(b"BFP") => "Podman adapter",
130 Some(b"BFQ") => "Quadlet adapter",
131 Some(b"BFR") => "runtime reconstruction",
132 _ => "BoxFerry engine",
133 }
134 }
135
136 #[must_use]
138 pub const fn description(self) -> &'static str {
139 self.description
140 }
141
142 #[must_use]
144 pub const fn help(self) -> &'static str {
145 self.help
146 }
147
148 pub fn diagnostic_code(self) -> Result<DiagnosticCode, InvalidDiagnosticCode> {
154 DiagnosticCode::new(self.code)
155 }
156}
157
158macro_rules! rule {
159 ($id:ident, $code:literal, $name:literal, $severity:ident, $description:literal, $help:literal) => {
160 DiagnosticRule {
161 id: RuleId::$id,
162 code: $code,
163 name: $name,
164 default_severity: Severity::$severity,
165 description: $description,
166 help: $help,
167 }
168 };
169}
170
171pub const RULES: &[DiagnosticRule] = &[
173 rule!(
174 ComposeModelInvalid,
175 "BFC0001",
176 "compose-model-invalid",
177 Error,
178 "Compose intent could not be represented by the neutral application model.",
179 "Correct the reported Compose value or remove the conflicting declaration."
180 ),
181 rule!(
182 ComposeProfileRequired,
183 "BFC0002",
184 "compose-profile-selection-required",
185 Error,
186 "Profiled Compose services require an explicit profile selection.",
187 "Select profiles explicitly with --profile, or use --all-profiles only after reviewing mutually exclusive services."
188 ),
189 rule!(
190 ComposeProfileMismatch,
191 "BFC0003",
192 "compose-profile-selection-mismatch",
193 Error,
194 "The Compose profile selection is invalid or belongs to another merged project.",
195 "Recreate the profile selection from the same merged Compose project."
196 ),
197 rule!(
198 ComposeIntentUnsupported,
199 "BFC0004",
200 "compose-intent-unsupported",
201 Warning,
202 "Compose intent is not represented by the current neutral-model subset.",
203 "Review the named feature; use --loss-policy partial only when omitting that intent is acceptable."
204 ),
205 rule!(
206 ComposeValueInvalid,
207 "BFC0005",
208 "compose-value-invalid",
209 Error,
210 "A Compose value cannot be represented safely in the neutral model.",
211 "Correct the reported value before converting again."
212 ),
213 rule!(
214 ComposeTargetInvalid,
215 "BFC0006",
216 "compose-target-invalid",
217 Error,
218 "The requested Compose output target is invalid.",
219 "Select a supported Compose target profile and compatible version inputs."
220 ),
221 rule!(
222 ComposeOutputUnsupported,
223 "BFC0007",
224 "compose-output-unsupported",
225 Warning,
226 "Neutral application intent is not represented in generated Compose output.",
227 "Review the named subject; use --loss-policy partial only when the omitted intent is acceptable."
228 ),
229 rule!(
230 ComposeGenerationFailed,
231 "BFC0008",
232 "compose-generation-failed",
233 Error,
234 "ComposeLens rejected generated Compose output.",
235 "Correct the reported neutral value or target choice and retry."
236 ),
237 rule!(
238 ComposeCompatibilityConstraint,
239 "BFC0009",
240 "compose-compatibility-constraint",
241 Warning,
242 "Generated Compose syntax has a target-specific compatibility constraint.",
243 "Review the target-specific classification before authorizing non-exact output."
244 ),
245 rule!(
246 ComposeUnsetVariable,
247 "BFC0101",
248 "compose-unset-variable",
249 Warning,
250 "A Compose interpolation variable is not set.",
251 "Provide the missing value with --env-file FILE or --env NAME=VALUE."
252 ),
253 rule!(
254 ComposeRequiredVariable,
255 "BFC0102",
256 "compose-required-variable",
257 Error,
258 "A required Compose interpolation variable is not set.",
259 "Provide the required value with --env-file FILE or --env NAME=VALUE."
260 ),
261 rule!(
262 ComposeInterpolationInvalid,
263 "BFC0103",
264 "compose-interpolation-invalid",
265 Error,
266 "A Compose interpolation expression is invalid.",
267 "Correct the interpolation expression and retry."
268 ),
269 rule!(
270 ComposeInterpolationNestingLimit,
271 "BFC0104",
272 "compose-interpolation-nesting-limit",
273 Error,
274 "A Compose interpolation expression exceeds the supported nesting limit.",
275 "Simplify the interpolation expression before converting."
276 ),
277 rule!(
278 ComposeUnresolvedVariable,
279 "BFC0105",
280 "compose-unresolved-variable",
281 Warning,
282 "A Compose variable expression remains unresolved at the adapter boundary.",
283 "Use --interpolate and provide missing values with --env-file FILE or --env NAME=VALUE; partial authorization applies only when the affected intent can be omitted."
284 ),
285 rule!(
286 ComposeNativeError,
287 "BFC0197",
288 "compose-native-error",
289 Error,
290 "ComposeLens reported a native Compose error.",
291 "Use the retained source code and source span to correct the Compose document."
292 ),
293 rule!(
294 ComposeNativeWarning,
295 "BFC0198",
296 "compose-native-warning",
297 Warning,
298 "ComposeLens reported a native Compose warning.",
299 "Review the retained source code and source span before accepting the document."
300 ),
301 rule!(
302 ComposeNativeNote,
303 "BFC0199",
304 "compose-native-note",
305 Note,
306 "ComposeLens reported native Compose information.",
307 "Review the retained source code when more context is needed."
308 ),
309 rule!(
310 DockerDataInvalid,
311 "BFD0001",
312 "docker-inspection-invalid",
313 Error,
314 "Docker inspection data is malformed or contradictory.",
315 "Inspect the selected Docker response and retry with a supported, valid payload."
316 ),
317 rule!(
318 DockerConfigurationUnsupported,
319 "BFD0002",
320 "docker-configuration-unsupported",
321 Warning,
322 "Docker inspection contains meaningful configuration outside the current model.",
323 "Review the named field; use partial output only when omission is acceptable."
324 ),
325 rule!(
326 DockerVersionUnsupported,
327 "BFD0003",
328 "docker-api-version-unsupported",
329 Error,
330 "The Docker Engine API version is outside the reviewed range.",
331 "Select a Docker Engine API version covered by BoxFerry."
332 ),
333 rule!(
334 DockerRelationshipMissing,
335 "BFD0004",
336 "docker-relationship-missing",
337 Warning,
338 "Docker inspection references a resource absent from the supplied snapshot.",
339 "Inspect and supply the referenced resource or accept partial reconstruction."
340 ),
341 rule!(
342 OrchestrationFailed,
343 "BFO1000",
344 "orchestration-failed",
345 Error,
346 "BoxFerry could not complete the requested orchestration stage.",
347 "Review the reason and correct the command inputs before retrying."
348 ),
349 rule!(
350 InputReadFailed,
351 "BFO1001",
352 "input-read-failed",
353 Error,
354 "An explicitly selected input could not be read.",
355 "Check that the input exists, is a readable regular file, and is not a symlink."
356 ),
357 rule!(
358 ComposeProjectRootInvalid,
359 "BFO1002",
360 "compose-project-root-invalid",
361 Error,
362 "The Compose project root could not be resolved.",
363 "Provide an existing absolute --project-directory or correct the input location."
364 ),
365 rule!(
366 PodmanTargetSelectionInvalid,
367 "BFO1003",
368 "podman-target-selection-invalid",
369 Error,
370 "The requested Podman target range could not be resolved.",
371 "Use major.minor or major.minor.patch values within the reviewed range."
372 ),
373 rule!(
374 InterpolationInputInvalid,
375 "BFO1004",
376 "interpolation-input-invalid",
377 Error,
378 "Compose interpolation inputs could not be resolved safely.",
379 "Correct the named --env-file or --env input and retry."
380 ),
381 rule!(
382 ComposeLoadFailed,
383 "BFO1005",
384 "compose-load-failed",
385 Error,
386 "Compose input could not be loaded or merged.",
387 "Correct the reported Compose source diagnostic before retrying."
388 ),
389 rule!(
390 ConversionFailed,
391 "BFO1006",
392 "conversion-failed",
393 Error,
394 "Source import or target conversion failed.",
395 "Correct the reported rule occurrences before retrying."
396 ),
397 rule!(
398 QuadletParseFailed,
399 "BFO1007",
400 "quadlet-parse-failed",
401 Error,
402 "Quadlet input could not be parsed into the native document boundary.",
403 "Correct the retained native diagnostics before retrying."
404 ),
405 rule!(
406 OutputDirectoryNotEmpty,
407 "BFO2001",
408 "output-directory-not-empty",
409 Error,
410 "The selected output directory is not empty.",
411 "Empty the selected --output-directory or choose a new path."
412 ),
413 rule!(
414 OutputPathInvalid,
415 "BFO2002",
416 "output-path-invalid",
417 Error,
418 "The selected output path is not a usable non-symlink directory.",
419 "Choose an absent path or an existing empty, non-symlink directory."
420 ),
421 rule!(
422 OutputWriteFailed,
423 "BFO2003",
424 "output-write-failed",
425 Error,
426 "Generated output could not be written safely.",
427 "Check parent-directory existence, permissions, free space, and path conflicts."
428 ),
429 rule!(
430 ReportWriteFailed,
431 "BFO3001",
432 "report-write-failed",
433 Error,
434 "The structured report file could not be written safely.",
435 "Choose a new writable --report-file path."
436 ),
437 rule!(
438 SupportBundleWriteFailed,
439 "BFO3002",
440 "support-bundle-write-failed",
441 Error,
442 "The diagnostic support bundle could not be written safely.",
443 "Choose a writable --error-report-directory with space for a new archive."
444 ),
445 rule!(
446 PodmanDataInvalid,
447 "BFP0001",
448 "podman-inspection-invalid",
449 Error,
450 "Podman inspection data is malformed or contradictory.",
451 "Inspect the selected Podman response and retry with a supported, valid payload."
452 ),
453 rule!(
454 PodmanConfigurationUnsupported,
455 "BFP0002",
456 "podman-configuration-unsupported",
457 Warning,
458 "Podman inspection contains meaningful configuration outside the current model.",
459 "Review the named field; use partial output only when omission is acceptable."
460 ),
461 rule!(
462 PodmanVersionUnsupported,
463 "BFP0003",
464 "podman-version-unsupported",
465 Error,
466 "The Podman version is outside the reviewed range.",
467 "Select a Podman version covered by BoxFerry."
468 ),
469 rule!(
470 PodmanRelationshipMissing,
471 "BFP0004",
472 "podman-relationship-missing",
473 Warning,
474 "Podman inspection references a resource absent from the supplied snapshot.",
475 "Inspect and supply the referenced resource or accept partial reconstruction."
476 ),
477 rule!(
478 QuadletTargetInvalid,
479 "BFQ0001",
480 "quadlet-target-invalid",
481 Error,
482 "The requested Quadlet target or Podman range is invalid.",
483 "Select the podman target and a version range covered by QuadletLens."
484 ),
485 rule!(
486 QuadletOpenEndedTarget,
487 "BFQ0002",
488 "quadlet-open-ended-target",
489 Note,
490 "An omitted Podman maximum extends beyond verified compatibility evidence.",
491 "Set --podman-maximum-version for a finite compatibility claim."
492 ),
493 rule!(
494 QuadletOutputUnsupported,
495 "BFQ0003",
496 "quadlet-output-unsupported",
497 Warning,
498 "Neutral application intent is not represented by the current Quadlet subset.",
499 "Review the named subject; use --loss-policy partial only when omission is acceptable."
500 ),
501 rule!(
502 QuadletValueInvalid,
503 "BFQ0004",
504 "quadlet-value-invalid",
505 Error,
506 "A neutral value cannot be emitted safely as native Quadlet syntax.",
507 "Correct or explicitly map the reported value before converting again."
508 ),
509 rule!(
510 QuadletGenerationFailed,
511 "BFQ0005",
512 "quadlet-generation-failed",
513 Error,
514 "QuadletLens rejected generated native output.",
515 "Correct the reported value or dependency and retry."
516 ),
517 rule!(
518 QuadletCapabilityUnavailable,
519 "BFQ0006",
520 "quadlet-capability-unavailable",
521 Warning,
522 "A required Quadlet capability is unavailable for part of the target range.",
523 "Narrow the Podman target range or accept the documented partial result."
524 ),
525 rule!(
526 QuadletGroupingApproximation,
527 "BFQ0007",
528 "quadlet-grouping-approximation",
529 Warning,
530 "Generated Quadlet grouping changes source service isolation.",
531 "Use --quadlet-grouping pod only when the shared-pod semantics are acceptable."
532 ),
533 rule!(
534 QuadletDependencyUnsupported,
535 "BFQ0008",
536 "quadlet-dependency-unsupported",
537 Warning,
538 "A service dependency cannot be represented exactly by Quadlet and systemd.",
539 "Review the dependency condition and complete unsupported behavior manually."
540 ),
541 rule!(
542 QuadletRestartApproximation,
543 "BFQ0009",
544 "quadlet-restart-policy-approximation",
545 Warning,
546 "Container restart behavior is approximated by the systemd service manager.",
547 "Use --loss-policy approximate only after accepting the documented systemd behavior difference."
548 ),
549 rule!(
550 QuadletEnvironmentFileApproximation,
551 "BFQ0010",
552 "quadlet-environment-file-approximation",
553 Warning,
554 "Environment-file parsing is delegated to Podman.",
555 "Verify the environment file with the target Podman version before deployment."
556 ),
557 rule!(
558 QuadletGroupingInvalid,
559 "BFQ0011",
560 "quadlet-grouping-invalid",
561 Error,
562 "The requested Quadlet grouping cannot preserve the application topology.",
563 "Keep separate containers or correct the reported grouping conflict."
564 ),
565 rule!(
566 QuadletDependencyInvalid,
567 "BFQ0012",
568 "quadlet-dependency-invalid",
569 Error,
570 "The service or image-artifact dependency graph is invalid.",
571 "Correct missing or cyclic dependencies before converting."
572 ),
573 rule!(
574 QuadletCapabilityDeprecated,
575 "BFQ0013",
576 "quadlet-capability-deprecated",
577 Note,
578 "A required Quadlet capability is deprecated for part of the target range.",
579 "Review the target range and plan migration away from the deprecated capability."
580 ),
581 rule!(
582 QuadletUnresolvedVariable,
583 "BFQ0014",
584 "quadlet-unresolved-source-variable",
585 Error,
586 "A source variable expression cannot be emitted as a Quadlet value.",
587 "Resolve source variables before conversion; for Compose input, use --interpolate and provide missing values with --env-file FILE or --env NAME=VALUE."
588 ),
589 rule!(
590 QuadletSourceInvalid,
591 "BFQ1001",
592 "quadlet-source-invalid",
593 Error,
594 "The Quadlet document set is invalid.",
595 "Correct native Quadlet syntax and document-set errors before converting."
596 ),
597 rule!(
598 QuadletModelInvalid,
599 "BFQ1002",
600 "quadlet-model-invalid",
601 Error,
602 "Quadlet intent cannot be represented by the neutral application model.",
603 "Correct the reported native value or relationship."
604 ),
605 rule!(
606 QuadletInputUnsupported,
607 "BFQ1003",
608 "quadlet-input-unsupported",
609 Warning,
610 "Quadlet intent is outside the current neutral-model importer subset.",
611 "Review the named key; use --loss-policy partial only when omission is acceptable."
612 ),
613 rule!(
614 QuadletInputApproximation,
615 "BFQ1004",
616 "quadlet-input-approximation",
617 Warning,
618 "Quadlet intent is approximated in the neutral model.",
619 "Review the documented semantic difference before authorizing approximate output."
620 ),
621 rule!(
622 QuadletNativeSyntax,
623 "BFQ1101",
624 "quadlet-native-syntax",
625 Error,
626 "QuadletLens reported a native syntax diagnostic.",
627 "Use the retained source code and source span to correct the Quadlet unit."
628 ),
629 rule!(
630 QuadletNativeModel,
631 "BFQ1102",
632 "quadlet-native-model",
633 Error,
634 "QuadletLens reported a native model diagnostic.",
635 "Use the retained source code and source span to correct the Quadlet value."
636 ),
637 rule!(
638 QuadletNativeDocumentSet,
639 "BFQ1103",
640 "quadlet-native-document-set",
641 Error,
642 "QuadletLens reported a native document-set diagnostic.",
643 "Correct missing, ambiguous, or invalid cross-document relationships."
644 ),
645 rule!(
646 QuadletNativeFailure,
647 "BFQ1104",
648 "quadlet-native-failure",
649 Error,
650 "QuadletLens could not construct the native input boundary.",
651 "Correct the reported input and native stage before retrying."
652 ),
653 rule!(
654 RuntimeReconstructionUncertain,
655 "BFR0001",
656 "runtime-reconstruction-uncertain",
657 Warning,
658 "Runtime inspection cannot prove the original authored definition.",
659 "Review the reconstructed definition and every field-level decision before deployment."
660 ),
661 rule!(
662 RuntimeOverrideInferred,
663 "BFR0002",
664 "runtime-override-inferred",
665 Warning,
666 "A runtime override was inferred by comparison with image defaults.",
667 "Review the inferred override before generating a reusable definition."
668 ),
669 rule!(
670 RuntimeComparisonIncomplete,
671 "BFR0003",
672 "runtime-comparison-incomplete",
673 Warning,
674 "Runtime-to-image comparison evidence is incomplete.",
675 "Supply the missing image evidence or review the affected field manually."
676 ),
677 rule!(
678 RuntimeOwnershipUncertain,
679 "BFR0004",
680 "runtime-ownership-uncertain",
681 Warning,
682 "Runtime resource lifecycle ownership is uncertain.",
683 "Choose application-owned or external lifecycle explicitly."
684 ),
685 rule!(
686 RuntimePodRelationshipIncomplete,
687 "BFR0005",
688 "runtime-pod-relationship-incomplete",
689 Warning,
690 "Runtime pod relationship evidence is incomplete or non-portable.",
691 "Review and explicitly resolve the service-group lifecycle."
692 ),
693 rule!(
694 RuntimeImageMissing,
695 "BFR0006",
696 "runtime-image-missing",
697 Warning,
698 "A runtime container has no reconstructable image reference.",
699 "Supply a reviewed image reference or return to an authored source definition."
700 ),
701 rule!(
702 RuntimeModelInvalid,
703 "BFR0007",
704 "runtime-model-invalid",
705 Error,
706 "Runtime observations cannot form a valid neutral application model.",
707 "Correct the reported observation or resource identity."
708 ),
709 rule!(
710 RuntimeGroupConflict,
711 "BFR0008",
712 "runtime-group-conflict",
713 Error,
714 "Runtime group relationship evidence is contradictory.",
715 "Correct or narrow the selected runtime snapshot."
716 ),
717 rule!(
718 RuntimeLifecycleResolved,
719 "BFR0009",
720 "runtime-lifecycle-resolved",
721 Warning,
722 "Caller policy resolved lifecycle intent that inspection could not prove.",
723 "Review the explicit lifecycle override before deployment."
724 ),
725 rule!(
726 RuntimeManagedMetadata,
727 "BFR0010",
728 "runtime-managed-metadata",
729 Warning,
730 "Runtime-managed metadata is unsafe to re-author as application metadata.",
731 "Keep the metadata as evidence and do not copy it into generated definitions."
732 ),
733];
734
735impl RuleId {
736 #[must_use]
738 pub const fn definition(self) -> &'static DiagnosticRule {
739 match self {
740 Self::ComposeModelInvalid => &RULES[0],
741 Self::ComposeProfileRequired => &RULES[1],
742 Self::ComposeProfileMismatch => &RULES[2],
743 Self::ComposeIntentUnsupported => &RULES[3],
744 Self::ComposeValueInvalid => &RULES[4],
745 Self::ComposeTargetInvalid => &RULES[5],
746 Self::ComposeOutputUnsupported => &RULES[6],
747 Self::ComposeGenerationFailed => &RULES[7],
748 Self::ComposeCompatibilityConstraint => &RULES[8],
749 Self::ComposeUnsetVariable => &RULES[9],
750 Self::ComposeRequiredVariable => &RULES[10],
751 Self::ComposeInterpolationInvalid => &RULES[11],
752 Self::ComposeInterpolationNestingLimit => &RULES[12],
753 Self::ComposeUnresolvedVariable => &RULES[13],
754 Self::ComposeNativeError => &RULES[14],
755 Self::ComposeNativeWarning => &RULES[15],
756 Self::ComposeNativeNote => &RULES[16],
757 Self::DockerDataInvalid => &RULES[17],
758 Self::DockerConfigurationUnsupported => &RULES[18],
759 Self::DockerVersionUnsupported => &RULES[19],
760 Self::DockerRelationshipMissing => &RULES[20],
761 Self::OrchestrationFailed => &RULES[21],
762 Self::InputReadFailed => &RULES[22],
763 Self::ComposeProjectRootInvalid => &RULES[23],
764 Self::PodmanTargetSelectionInvalid => &RULES[24],
765 Self::InterpolationInputInvalid => &RULES[25],
766 Self::ComposeLoadFailed => &RULES[26],
767 Self::ConversionFailed => &RULES[27],
768 Self::QuadletParseFailed => &RULES[28],
769 Self::OutputDirectoryNotEmpty => &RULES[29],
770 Self::OutputPathInvalid => &RULES[30],
771 Self::OutputWriteFailed => &RULES[31],
772 Self::ReportWriteFailed => &RULES[32],
773 Self::SupportBundleWriteFailed => &RULES[33],
774 Self::PodmanDataInvalid => &RULES[34],
775 Self::PodmanConfigurationUnsupported => &RULES[35],
776 Self::PodmanVersionUnsupported => &RULES[36],
777 Self::PodmanRelationshipMissing => &RULES[37],
778 Self::QuadletTargetInvalid => &RULES[38],
779 Self::QuadletOpenEndedTarget => &RULES[39],
780 Self::QuadletOutputUnsupported => &RULES[40],
781 Self::QuadletValueInvalid => &RULES[41],
782 Self::QuadletGenerationFailed => &RULES[42],
783 Self::QuadletCapabilityUnavailable => &RULES[43],
784 Self::QuadletGroupingApproximation => &RULES[44],
785 Self::QuadletDependencyUnsupported => &RULES[45],
786 Self::QuadletRestartApproximation => &RULES[46],
787 Self::QuadletEnvironmentFileApproximation => &RULES[47],
788 Self::QuadletGroupingInvalid => &RULES[48],
789 Self::QuadletDependencyInvalid => &RULES[49],
790 Self::QuadletCapabilityDeprecated => &RULES[50],
791 Self::QuadletUnresolvedVariable => &RULES[51],
792 Self::QuadletSourceInvalid => &RULES[52],
793 Self::QuadletModelInvalid => &RULES[53],
794 Self::QuadletInputUnsupported => &RULES[54],
795 Self::QuadletInputApproximation => &RULES[55],
796 Self::QuadletNativeSyntax => &RULES[56],
797 Self::QuadletNativeModel => &RULES[57],
798 Self::QuadletNativeDocumentSet => &RULES[58],
799 Self::QuadletNativeFailure => &RULES[59],
800 Self::RuntimeReconstructionUncertain => &RULES[60],
801 Self::RuntimeOverrideInferred => &RULES[61],
802 Self::RuntimeComparisonIncomplete => &RULES[62],
803 Self::RuntimeOwnershipUncertain => &RULES[63],
804 Self::RuntimePodRelationshipIncomplete => &RULES[64],
805 Self::RuntimeImageMissing => &RULES[65],
806 Self::RuntimeModelInvalid => &RULES[66],
807 Self::RuntimeGroupConflict => &RULES[67],
808 Self::RuntimeLifecycleResolved => &RULES[68],
809 Self::RuntimeManagedMetadata => &RULES[69],
810 }
811 }
812}
813
814#[must_use]
816pub fn find_rule(value: &str) -> Option<&'static DiagnosticRule> {
817 RULES
818 .iter()
819 .find(|rule| rule.code.eq_ignore_ascii_case(value) || rule.name.eq_ignore_ascii_case(value))
820}
821
822#[cfg(test)]
823mod tests {
824 use std::collections::BTreeSet;
825
826 use super::{RULES, RuleId, find_rule};
827
828 #[test]
829 fn catalogue_codes_names_and_typed_indices_are_unique_and_valid() -> Result<(), String> {
830 let mut codes = BTreeSet::new();
831 let mut names = BTreeSet::new();
832 let mut ids = BTreeSet::new();
833 for rule in RULES {
834 rule.diagnostic_code().map_err(|error| error.to_string())?;
835 assert!(ids.insert(rule.id()), "duplicate typed id {:?}", rule.id());
836 assert_eq!(rule.id().definition(), rule);
837 assert!(codes.insert(rule.code()), "duplicate code {}", rule.code());
838 assert!(names.insert(rule.name()), "duplicate name {}", rule.name());
839 assert!(!rule.description().is_empty());
840 assert!(!rule.help().is_empty());
841 assert_ne!(
842 rule.owner(),
843 "BoxFerry engine",
844 "uncatalogued namespace for {}",
845 rule.code()
846 );
847 }
848 assert!(RULES.windows(2).all(|pair| pair[0].code() < pair[1].code()));
849 assert_eq!(RuleId::OutputWriteFailed.definition().code(), "BFO2003");
850 assert_eq!(find_rule("quadlet-restart-policy-approximation"), find_rule("BFQ0009"));
851 Ok(())
852 }
853}