PureXML/Sources/Parsing/ReadResult.swift
Mihaela Mihaljevic b1d82e533d feat(parsing): recovering reader that never crashes on invalid input
Slice 2 of #92. Add PureXML.read, a deterministic recovering reader: it never
throws and never traps, returning the maximal best-effort tree plus one located
Diagnostic per problem.

- ReadResult { node, diagnostics } is the Equatable result type, so a
  deterministic read can be compared whole.
- Diagnostic carries a message and a structured source Mark; a thrown ParseError
  maps to one directly (ParseError.mark exposes the position each case carries).
- The reader resynchronizes after a failure (EventReader.recover(from:)):
  progress-aware, so a failure that already consumed the bad construct retries
  from there, while a stuck failure skips to the next markup start; it always
  advances when input remains, guaranteeing termination.
- The tree builder assembles leniently: elements left open by truncation are
  closed, an end tag with no open element is dropped. Well-formed input yields
  exactly parse(_:)'s tree with no diagnostics.

Tests pin exact trees and diagnostics for truncation, mismatched tags, and stray
junk, prove determinism (same bytes give an identical ReadResult), and fuzz 400
seeded inputs (arbitrary plus truncated valid docs) asserting no crash.

Recovery here is resync-based; finer in-reader recovery (keep the element on a
duplicate attribute, pop to a matching open tag) is the next slice.
2026-06-08 12:15:18 +02:00

19 lines
741 B
Swift

public extension PureXML.Parsing {
/// The outcome of a recovering read: the best-effort node tree and every
/// located ``Diagnostic`` found along the way. Equatable, so a deterministic
/// read can be compared whole.
struct ReadResult: Equatable, Sendable {
/// The best-effort tree. Always a document node; as complete as recovery
/// allowed.
public var node: PureXML.Model.Node
/// One diagnostic per problem, in source order. Empty when the input was
/// well-formed.
public var diagnostics: [Diagnostic]
public init(node: PureXML.Model.Node, diagnostics: [Diagnostic]) {
self.node = node
self.diagnostics = diagnostics
}
}
}