PureXML/Sources/Validation/DTDDefaults.swift
Mihaela Mihaljevic 380160cc2b refactor(validation): decompose XSD instance validation into composable rules
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.
2026-07-12 14:12:17 +02:00

50 lines
2.2 KiB
Swift

public extension PureXML.Validation.DTDSchema {
/// Returns `node` with DTD attribute defaults applied: every element gains any
/// attribute its `<!ATTLIST>` declares with a default or `#FIXED` value and
/// that the element omits. `#REQUIRED` and `#IMPLIED` add nothing. The injected
/// values are the normalized defaults from the declaration, so an editor reads
/// the same effective attributes a validating processor would see.
func applyingDefaults(to node: PureXML.Model.Node) -> PureXML.Model.Node {
// Rebuilt bottom-up (iterative) so a deeply-nested document does not
// overflow the stack; each element gains its DTD-declared defaults once its
// children are rebuilt.
node.rebuildingBottomUp { original, rebuiltChildren in
switch original {
case let .element(element):
var result = element
result.children = rebuiltChildren
return .element(withDefaults(result))
case .document:
return .document(rebuiltChildren)
default:
return original
}
}
}
/// Adds the attributes `element`'s `<!ATTLIST>` declares with a default or
/// `#FIXED` value and that the element omits. `result.children` is assumed
/// already set by the caller.
private func withDefaults(_ element: PureXML.Model.Element) -> PureXML.Model.Element {
var result = element
guard let declarations = attributes[element.name.description] else { return result }
for declaration in declarations {
guard let value = Self.defaultValue(declaration) else { continue }
let present = result.attributes.contains {
$0.name.description == declaration.name || $0.name.localName == declaration.name
}
if !present {
result.attributes.append(PureXML.Model.Attribute(declaration.name, value))
}
}
return result
}
private static func defaultValue(_ declaration: PureXML.Validation.AttributeDeclaration) -> String? {
switch declaration.defaultDecl {
case let .value(value), let .fixed(value): value
case .required, .implied: nil
}
}
}