Skip to main content

imferno_core/xsd/
codes.rs

1//! Catalogue codes emitted by the runtime XSD validator.
2//!
3//! Five buckets cover the universe of XSD constraint violations. The
4//! translator (`super::translate`) classifies each uppsala diagnostic
5//! into one of these by message-pattern match; operators see stable
6//! catalogue codes and can override severity per code.
7
8use crate::diagnostics::codes::ValidationCode;
9use crate::diagnostics::{Category, Severity};
10
11/// Catalogue codes emitted by `xsd::validate_against_schema`.
12///
13/// Five broad buckets covering the universe of XSD constraint
14/// classes. Per-element refinement (e.g. mapping to a specific
15/// `CoreConstraintsCode::IssueDate` when the schema violation is on
16/// `<IssueDate>`) is a follow-up; the first cut keeps the translator
17/// simple and stable.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumIter)]
19pub enum XsdConstraintCode {
20    /// A required element is absent (XSD `minOccurs >= 1` violated).
21    ElementMissing,
22    /// An element appears outside its declared `xs:sequence` /
23    /// `xs:choice` content model.
24    UnexpectedElement,
25    /// A value violates a `xs:pattern` facet (regex restriction).
26    PatternInvalid,
27    /// A value violates its declared type (built-in or restriction).
28    TypeInvalid,
29    /// Any other XSD constraint failure; fallback bucket so
30    /// unrecognized uppsala messages still surface as a catalogue code
31    /// rather than being silently dropped.
32    SchemaConstraintFailed,
33}
34
35impl ValidationCode for XsdConstraintCode {
36    fn code(&self) -> &'static str {
37        match self {
38            Self::ElementMissing => "XSD/ElementMissing",
39            Self::UnexpectedElement => "XSD/UnexpectedElement",
40            Self::PatternInvalid => "XSD/PatternInvalid",
41            Self::TypeInvalid => "XSD/TypeInvalid",
42            Self::SchemaConstraintFailed => "XSD/SchemaConstraintFailed",
43        }
44    }
45
46    fn description(&self) -> &'static str {
47        match self {
48            Self::ElementMissing => "A required element is absent (XSD minOccurs violated).",
49            Self::UnexpectedElement => "An element appears outside its declared content model.",
50            Self::PatternInvalid => "A value violates an xs:pattern facet on its type.",
51            Self::TypeInvalid => {
52                "A value violates its declared XSD type (built-in or restriction)."
53            }
54            Self::SchemaConstraintFailed => "An XSD constraint failed; see message for details.",
55        }
56    }
57
58    fn default_severity(&self) -> Severity {
59        // Every XSD violation is at least an error per the schema. Operators
60        // can downgrade individual codes via per-code severity overrides.
61        Severity::Error
62    }
63
64    fn category(&self) -> Category {
65        Category::Schema
66    }
67
68    fn example(&self) -> Option<&'static str> {
69        Some(match self {
70            Self::ElementMissing =>
71                "<CompositionPlaylist>…</CompositionPlaylist>  <!-- missing required <EditRate> -->",
72            Self::UnexpectedElement =>
73                "<CompositionPlaylist><BogusTag/>…</CompositionPlaylist>  <!-- not in the schema sequence -->",
74            Self::PatternInvalid =>
75                "<Id>not-a-uuid</Id>  <!-- dcml:UUIDType pattern expects 'urn:uuid:...' -->",
76            Self::TypeInvalid =>
77                "<Size>not-a-number</Size>  <!-- declared xs:positiveInteger -->",
78            Self::SchemaConstraintFailed =>
79                "<EditRate>24 0</EditRate>  <!-- denominator violates a derived restriction -->",
80        })
81    }
82}
83
84impl XsdConstraintCode {
85    pub const ALL: &'static [Self] = &[
86        Self::ElementMissing,
87        Self::UnexpectedElement,
88        Self::PatternInvalid,
89        Self::TypeInvalid,
90        Self::SchemaConstraintFailed,
91    ];
92}
93
94impl From<XsdConstraintCode> for String {
95    fn from(c: XsdConstraintCode) -> String {
96        c.code().to_string()
97    }
98}