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.
60 lines
2.2 KiB
Swift
60 lines
2.2 KiB
Swift
extension PureXML.Schema {
|
|
/// Accumulates `xs:ID` and `xs:IDREF`/`xs:IDREFS` values across an instance
|
|
/// document and reports the document-scoped violations XSD requires: every ID
|
|
/// is unique across the document, and every IDREF/IDREFS item matches some ID.
|
|
/// These are not simple-type (lexical) checks; they need the whole document,
|
|
/// so the tree validator records here during its typed walk and reports after.
|
|
final class IDTracker {
|
|
private var seenIDs: Set<String> = []
|
|
private var duplicates: [PureXML.Validation.ValidationError] = []
|
|
private var references: [(value: String, path: [PureXML.Validation.PathKey])] = []
|
|
|
|
func recordID(_ value: String, at path: [PureXML.Validation.PathKey]) {
|
|
if !seenIDs.insert(value).inserted {
|
|
duplicates.append(.init(reason: "duplicate ID '\(value)'", at: path))
|
|
}
|
|
}
|
|
|
|
func recordReference(_ value: String, at path: [PureXML.Validation.PathKey]) {
|
|
references.append((value, path))
|
|
}
|
|
|
|
/// Duplicate-ID errors plus an error for every reference with no matching
|
|
/// ID. Resolution runs after the whole document is seen, so a reference may
|
|
/// point at an ID declared later.
|
|
func errors() -> [PureXML.Validation.ValidationError] {
|
|
var result = duplicates
|
|
for reference in references where !seenIDs.contains(reference.value) {
|
|
result.append(.init(reason: "IDREF '\(reference.value)' has no matching ID", at: reference.path))
|
|
}
|
|
return result
|
|
}
|
|
}
|
|
}
|
|
|
|
extension PureXML.Schema.SimpleType {
|
|
/// Whether this is `xs:ID` (or an atomic restriction of it).
|
|
var isID: Bool {
|
|
if case .atomic = variety {
|
|
return base == .id
|
|
}
|
|
return false
|
|
}
|
|
|
|
/// Whether this is `xs:IDREF` (or an atomic restriction of it).
|
|
var isIDReference: Bool {
|
|
if case .atomic = variety {
|
|
return base == .idref
|
|
}
|
|
return false
|
|
}
|
|
|
|
/// Whether this is `xs:IDREFS` (a list whose item type is `xs:IDREF`).
|
|
var isIDReferenceList: Bool {
|
|
if case let .list(item) = variety {
|
|
return item.base == .idref
|
|
}
|
|
return false
|
|
}
|
|
}
|