The two monolithic XSD content/identity rules only satisfied the OpenAPIKit validation idiom at the wrong granularity: no per-constraint removability, no isolable failing/near-miss tests. Split them into 32 named rules, each finding tagged at its origin and cached per XSDContext so the walk still runs once per document (the #101 decision against duplicating XSD's recursive type resolution still holds). Wired the streaming path's ID/IDREF and identity checks through the same framework instead of calling the validators imperatively, and added the unwrapAndLookup combinator the rule already named as required. Also includes the mandated repo-wide swiftformat/swiftlint pass.
38 lines
1.8 KiB
Swift
38 lines
1.8 KiB
Swift
extension PureXML.Schema.XSDParser {
|
|
/// Validity of an `any`/`anyAttribute` `namespace` constraint (XSD 1.0 Structures):
|
|
///
|
|
/// namespace ::= ('##any' | '##other')
|
|
/// | List of (anyURI | '##targetNamespace' | '##local')
|
|
///
|
|
/// So `##any` and `##other` stand alone, and a list admits only namespace URIs
|
|
/// and the `##targetNamespace`/`##local` tokens. A misspelled token (`##all`,
|
|
/// `##target`) or `##any`/`##other` placed inside a list (`##any ##other`) was
|
|
/// accepted, because the wildcard parser mapped any unrecognised token to a
|
|
/// literal URI; it is now rejected at compile time.
|
|
static func wildcardNamespaceErrors(_ node: XSDTree) -> [String] {
|
|
// The wildcard's own constraint is the unprefixed `namespace`; a prefixed
|
|
// (foreign) attribute that merely shares the local name is the author's own
|
|
// and is not checked, as `attributeApplicabilityErrors` also requires.
|
|
guard let raw = node.attributes.first(where: { $0.name.prefix == nil && $0.name.localName == "namespace" })?.value
|
|
else { return [] }
|
|
return validWildcardNamespace(raw)
|
|
? []
|
|
: ["the wildcard namespace '\(raw)' is not a valid namespace constraint"]
|
|
}
|
|
|
|
private static func validWildcardNamespace(_ raw: String) -> Bool {
|
|
let tokens = raw.split(whereSeparator: \.isWhitespace).map(String.init)
|
|
// `##any`/`##other` are valid only as the whole value, never as a list item.
|
|
if tokens == ["##any"] || tokens == ["##other"] {
|
|
return true
|
|
}
|
|
return tokens.allSatisfy { token in
|
|
switch token {
|
|
case "##targetNamespace", "##local": true
|
|
case "##any", "##other": false
|
|
default: !token.hasPrefix("##")
|
|
}
|
|
}
|
|
}
|
|
}
|