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.
48 lines
2.4 KiB
Swift
48 lines
2.4 KiB
Swift
public extension PureXML.Model.TreeNode {
|
|
/// Resolves a validation coding path to the node it addresses, navigating from
|
|
/// this node (typically the document) down by element name, using a sibling
|
|
/// index only when a name repeats. The same convention the validators build
|
|
/// paths with, so a ``PureXML/Validation/ValidationError``'s `codingPath` maps
|
|
/// to a node here, and the node's `sourceRange` gives the span to highlight.
|
|
/// An attribute step (`@name`) resolves to its owning element.
|
|
func node(at path: [PureXML.Validation.PathKey]) -> PureXML.Model.TreeNode? {
|
|
var current: PureXML.Model.TreeNode? = self
|
|
for key in path {
|
|
guard let node = current else { return nil }
|
|
if key.stringValue.hasPrefix("@") {
|
|
return node
|
|
}
|
|
let matches = node.children.filter { $0.kind == .element && $0.name?.description == key.stringValue }
|
|
let index = (key.intValue ?? 1) - 1
|
|
current = matches.indices.contains(index) ? matches[index] : nil
|
|
}
|
|
return current
|
|
}
|
|
|
|
/// The source span of the node a validation coding path addresses, when known.
|
|
func sourceRange(at path: [PureXML.Validation.PathKey]) -> PureXML.Parsing.SourceRange? {
|
|
node(at: path)?.sourceRange
|
|
}
|
|
|
|
/// The validation coding path from the document root to this node (a sibling
|
|
/// index only when a name repeats), for locating schema compile findings.
|
|
func validationCodingPath() -> [PureXML.Validation.PathKey] {
|
|
var steps: [PureXML.Validation.PathKey] = []
|
|
var current: PureXML.Model.TreeNode? = self
|
|
while let element = current, element.kind == .element, let name = element.name?.description {
|
|
steps.append(validationPathStep(for: element, named: name))
|
|
current = element.parent
|
|
}
|
|
return steps.reversed()
|
|
}
|
|
|
|
private func validationPathStep(for element: PureXML.Model.TreeNode, named name: String) -> PureXML.Validation.PathKey {
|
|
guard let parent = element.parent else { return .element(name) }
|
|
let siblings = parent.children.filter { $0.kind == .element && $0.name?.description == name }
|
|
guard siblings.count > 1, let index = siblings.firstIndex(where: { $0 === element }) else {
|
|
return .element(name)
|
|
}
|
|
return .element(name, index: index + 1)
|
|
}
|
|
}
|