PureXML/Sources/Validation/Validator.swift
Mihaela Mihaljevic 708390242c chore: scaffold PureXML pure-Swift XML package
Root Swift package mirroring PureYAML's structure, rules, and verification
gates. Includes the XML node model (Node, Element, Attribute, QualifiedName),
a working emitter (Serializer) with escaping and pretty/compact options, and
structural validation. The parser surface is stable; parse() raises
notImplemented until the scanner lands, pinned by a test.

- dependency-free, namespaced under PureXML
- portability and style gates carried over (style, namespacing, forbidden
  patterns, swiftformat, swiftlint, linux, wasm, windows CI)
- 11 tests passing
2026-06-07 23:20:20 +02:00

59 lines
2.2 KiB
Swift

public extension PureXML.Validation {
/// Path-aware validation over a parsed XML tree.
///
/// The default validator checks structural rules that do not depend on a
/// schema: an element must not declare the same attribute name twice. Schema
/// (DTD, XSD, RELAX NG) validation is intentionally out of scope for the
/// library target and is layered on by callers.
struct Validator: Sendable {
public init() {}
/// Collects validation issues for a node tree.
public func collect(_ node: PureXML.Model.Node) -> [Issue] {
var issues: [Issue] = []
walk(node, into: &issues)
return issues
}
/// Validates a node tree, throwing the first error-severity issue.
@discardableResult
public func validate(_ node: PureXML.Model.Node, strict: Bool = true) throws -> [Issue] {
let issues = collect(node)
if let error = issues.first(where: { $0.severity == .error }) {
throw error
}
if strict, let warning = issues.first(where: { $0.severity == .warning }) {
throw warning
}
return issues
}
private func walk(_ node: PureXML.Model.Node, into issues: inout [Issue]) {
switch node {
case let .document(children):
for child in children {
walk(child, into: &issues)
}
case let .element(element):
var seen: Set<String> = []
for attribute in element.attributes {
let key = attribute.name.description
if !seen.insert(key).inserted {
issues.append(.init(
severity: .error,
message: "duplicate attribute '\(key)' on <\(element.name.description)>",
))
}
}
for child in element.children {
walk(child, into: &issues)
}
case .text, .cdata, .comment, .processingInstruction:
break
}
}
}
}
extension PureXML.Validation.Issue: Swift.Error {}