Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Ixmllint Processor

Purpose

Checks that XML files are well-formed and, with schema, that they validate against an XSD schema. Native (in-process; no libxml2, no external tools). The Rust alternative to the xmllint processor: it reports what xmllint --noout and xmllint --noout --schema x.xsd report, at the same line, with the same verdict.

How It Works

Each file is decoded as its XML declaration says (UTF-8 by default; UTF-16, ISO-8859-1 and US-ASCII are understood, another declared encoding is an error), tokenized, and checked for everything xmllint checks: tag matching, duplicate attributes, entity and character references against the internal DTD subset, namespace declarations and prefixes, and the document’s shape. Problems are reported in xmllint’s format, one per line:

test.xml:3: parser error : Opening and ending tag mismatch: b line 2 and a
test.xml:6: parser error : Attribute b redefined
test.xml:1: namespace error : Namespace prefix x on b is not defined
test.xml:7: Schemas validity error : Element '{urn:keynote}slidex': This element is not expected. Expected is ( {urn:keynote}slide ).

The verdict is xmllint’s. A parser error (the file is not XML) or a Schemas validity error fails the file. A namespace error, namespace warning or parser warning is printed and the file passes, because xmllint exits 0 on those; strict = true makes them fail too.

Where xmllint reports a problem is reproduced as well: a duplicate attribute, an undeclared prefix or a schema violation is reported on the line where the element’s start tag ends (libxml2 checks the tag once it is complete and stamps the node with that line), an unfinished document on its last line, and only when nothing else went wrong first.

Schema validation

schema names an XSD file, relative to the project root, like --schema. Every file must validate against it; the schema is read once per build. The schema file is an input of every product, so a change to it re-checks every file; there is no need to list it in dep_inputs.

The validator implements the schema constructs in use: global and local elements, named and anonymous complex types with sequence, choice and all content models (with minOccurs/maxOccurs), any, mixed and simple content, attributes with use and fixed, anyAttribute, simple types restricted by the standard facets (enumeration, pattern, lengths, bounds, digits, whiteSpace), list and union, extension of simple and complex types, include, and the built-in types from xs:string to xs:dateTime. A schema using anything else (group, attributeGroup, import, redefine, key/unique, substitution groups, abstract, complex-type restriction, xsi:type in a document) is a build failure naming the construct and its line: a schema that is only half enforced would pass files xmllint rejects. Keep such a file on the xmllint processor, or extend src/engines/xmllint/xsd.rs.

Messages follow xmllint’s wording (This element is not expected. Expected is ( ... )., The attribute 'a' is required but missing., 'x' is not a valid value of the atomic type 'xs:int'., [facet 'enumeration'] The value 'v' is not an element of the set {'a', 'b'}.).

Fidelity

Checked against xmllint (libxml2 2.15) over the fleet’s 6256 tracked XML and SVG files plus 58 hand-made edge cases: every file gets the same verdict, every problem is reported at the same line with the same class, and the messages read the same. Schema validation was checked over the one fleet schema (java-keynote) with its presentations and eight mutations of them: identical reports, line for line.

Two differences remain. After a lexical error (a bad comment, an invalid character), xmllint keeps parsing and may report further problems; ixmllint stops at that error, which is already fatal. And xmllint’s parser does not load external DTDs, nor does ixmllint, so neither validates against a DTD; --valid, --dtdvalid and --relaxng have no counterpart here.

Source Files

  • Input: .xml and .svg files under src_dirs
  • Output: none (checker)

The default src_dirs is empty, so a stanza with no src_dirs or src_files checks nothing: name the directories ([""] for the whole project) or files to check.

Configuration

[processor.checker.ixmllint]
src_dirs = ["src"]

[processor.checker.ixmllint.presentations]
src_dirs = ["presentations"]
schema = "xsd/keynote.xsd"
KeyTypeDefaultDescription
schemastring""XSD schema to validate every file against, like xmllint --schema; relative to the project root. Empty: well-formedness only
strictboolfalseFail on namespace errors and parser warnings too (xmllint exits 0 on them)
src_dirsstring[][]Directories to scan
src_extensionsstring[][".xml", ".svg"]File extensions to check
dep_inputsstring[][]Extra files whose changes trigger rebuilds (schema is tracked already)

Batch support

Files are checked individually within a batch, so one bad file fails only its own product.

Clean behavior

This processor is a Checker — rsconstruct clean outputs is a no-op for it (checkers produce no outputs). See Clean behavior and rsconstruct clean.