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.
72 lines
3.0 KiB
Swift
72 lines
3.0 KiB
Swift
extension PureXML.Schema.IdentityValidator {
|
|
func simpleType(of declaration: PureXML.Schema.ElementType) -> PureXML.Schema.SimpleType? {
|
|
switch declaration {
|
|
case let .simple(simple): simple
|
|
case let .complex(complex):
|
|
if case let .simpleContent(simple) = complex.content {
|
|
simple
|
|
} else {
|
|
nil
|
|
}
|
|
case .typeReference: nil
|
|
}
|
|
}
|
|
|
|
static func effectiveType(of value: PureXML.XPath.Value) -> PureXML.Schema.SimpleType? {
|
|
guard let nodes = value.nodes, case let .tree(treeNode)? = nodes.first,
|
|
treeNode.kind == .element, let local = xsiTypeLocalName(of: treeNode),
|
|
let builtin = PureXML.Schema.BuiltinType(rawValue: local)
|
|
else { return nil }
|
|
return PureXML.Schema.SimpleType(base: builtin)
|
|
}
|
|
|
|
/// The local name of a tree node's `xsi:type` attribute value, recognized by
|
|
/// the schema-instance namespace or the conventional `xsi` prefix, or nil.
|
|
static func xsiTypeLocalName(of node: PureXML.Model.TreeNode) -> String? {
|
|
let schemaInstance = "http://www.w3.org/2001/XMLSchema-instance"
|
|
guard let attribute = node.attributes.first(where: {
|
|
$0.name.localName == "type" && ($0.name.namespaceURI == schemaInstance || $0.name.prefix == "xsi")
|
|
}) else { return nil }
|
|
return attribute.value.split(separator: ":").last.map(String.init) ?? attribute.value
|
|
}
|
|
|
|
func label(_ constraint: PureXML.Schema.IdentityConstraint) -> String {
|
|
constraint.kind == .key ? "key" : "unique"
|
|
}
|
|
|
|
/// Whether a constraint collects tuples (`key`/`unique`) rather than
|
|
/// referencing them (`keyref`).
|
|
func isNonRef(_ constraint: PureXML.Schema.IdentityConstraint) -> Bool {
|
|
if case .keyref = constraint.kind {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
/// The coding-path steps from the constraint-bearing `container` down to a
|
|
/// selected `target`, so an identity error locates the actual offending
|
|
/// element rather than the element that merely declares the constraint.
|
|
/// Returns an empty path if `target` is not a descendant of `container`.
|
|
func steps(from container: PureXML.Model.TreeNode, to target: PureXML.Model.TreeNode) -> [PureXML.Validation.PathKey] {
|
|
var chain: [PureXML.Model.TreeNode] = []
|
|
var current = target
|
|
while current !== container {
|
|
guard let parent = current.parent else { return [] }
|
|
chain.append(current)
|
|
current = parent
|
|
}
|
|
var steps: [PureXML.Validation.PathKey] = []
|
|
var parent = container
|
|
for node in chain.reversed() {
|
|
let name = node.name?.description ?? ""
|
|
if let index = stepIndex.position(of: node, under: parent, name: name) {
|
|
steps.append(.element(name, index: index))
|
|
} else {
|
|
steps.append(.element(name))
|
|
}
|
|
parent = node
|
|
}
|
|
return steps
|
|
}
|
|
}
|