Slice 1 of #92. Introduce the OpenAPIKit-idiom validation framework, replacing
the imperative if-tree structural validator:
- ValidationError (reason + coding path) renders "<reason> at path: root/row/cell";
ValidationErrorCollection gathers every failure and is thrown once.
- Validation<Subject, Document> is the atomic, composable rule value, with the
multi-error and single-error Bool inits (the latter auto-forms "Failed to
satisfy: <description>"). Predicate-gated, type-directed via an erased
AnyValidation that guards an optional from matching a non-optional rule.
- The framework is parameterized over both Subject and Document so the structural,
DTD, XSD, and Schematron layers share one machinery (Document = Void for rules
that need only their subject).
- Validator<Document> walks the node tree, building the coding path, with blank /
default factories and fluent validating / withoutValidating.
- The combinator algebra (&&, ||, comparison lifts, take, lift, unwrap, all) as
free functions returning closures.
The structural duplicate-attribute rule is reborn as Validation<Element, Void>.
Combinators and the erased wrapper are unit-tested as pure functions per the rule.
The legacy Validation.Issue stays only for the not-yet-migrated schema validators
and is retired in a later slice.
Add PureXML.Validation.Schematron: compile a Schematron schema (matching
schema/pattern/rule/assert/report by local name, so the ISO and legacy
namespaces both work) and validate a document. A rule's context XPath
selects the nodes it fires on; each assert/report test is evaluated relative
to the node via the XPath context-node API. A failed assert is an error, a
matched report a warning; within a pattern a node fires the first matching
rule. Adds XPath.Query.nodes(over:) returning matched tree nodes so one tree
can be queried repeatedly.
Closes#52Closes#53Closes#25
Complete the structural DTD validation layer. AttributeType now distinguishes
the ID family; the validator does a second pass after the structural walk
(since an IDREF may point forward to an ID later in the document):
- ID-typed attribute values must be unique across the document.
- IDREF values must each resolve to a declared ID.
- IDREFS values are split on whitespace and each token must resolve.
Reimplemented from the DTD ID/IDREF spec; no upstream code copied. 4 tests; 79
total.
Surface <!ATTLIST> declarations from the parser and validate attributes against
them. Reimplemented from the DTD attribute-list spec; no upstream code copied.
- DocumentType gains attributeLists (raw ATTLIST bodies per element); the
DoctypeScanner captures them and the EventReader plumbs them through.
- AttributeListParser parses a body into AttributeDeclaration values
(AttributeType: cdata or enumeration; AttributeDefault: required/implied/
fixed/value).
- DTDSchema now checks #REQUIRED presence, #FIXED value match (when present),
and enumerated-value membership, in addition to content models.
- Tokenized types (ID/IDREF/NMTOKEN) parse but are not yet value-enforced.
- 5 attribute tests; 75 total.
Surface the parsed <!DOCTYPE> internal subset's <!ELEMENT> declarations from the
parser and validate a tree against them. Reimplemented from the DTD content-model
spec; no upstream code copied.
- DoctypeScanner captures <!ELEMENT name model> declarations; the parser surfaces
them as Parsing.DocumentType (via parseWithDocumentType).
- Validation.ContentModel + ContentModelParser parse a model string (EMPTY, ANY,
(#PCDATA), mixed, and element content with sequence/choice and ?/*/+).
- ContentModelMatcher matches the child element-name sequence against a model as
a regular language (NFA-position matching).
- Validation.DTDSchema.validate reports issues for EMPTY/PCDATA/mixed violations,
element content with stray text, and child sequences that do not match.
- PureXML.validateAgainstInternalDTD(_:) parses and validates in one call.
- ATTLIST validation is out of scope. 9 tests; 70 total.
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