BarrierLab

Using html-conform as a dependency

The public API, the one option, error handling, and patterns for tests, tools and services.

The whole public API is two functions and a handful of types, all re-exported from the crate root:

ItemPurpose
check(html: &str)Check a complete document with the default options
check_with_options(html: &str, options: CheckOptions)Same, with explicit options
CheckOptionsinclude_parse_errors: bool (default true)
CheckReportfindings: Vec<Finding>, plus has_errors()
Findingrule_id, severity, message, location
SeverityError, Warning, Info
SourceLocationone-based line and column, zero-based byte_offset
CheckErrorthe document was not checked: too deeply nested, or a setup failure

Input

check() takes the complete document as a &str, so decoding bytes is up to the caller. For UTF-8 input, std::fs::read_to_string or String::from_utf8 is enough. The document is always parsed with HTML5 error recovery, the same way a browser would build its tree; there is no separate XML or XHTML mode.

Options

CheckOptions has one field. Set include_parse_errors to false to drop the recovered parser diagnostics (parser.html5) from the report:

use html_conform::{CheckOptions, check_with_options};

let options = CheckOptions {
    include_parse_errors: false,
};
let report = check_with_options(html, options)?;

Schema, assertion, script, CSP and table findings are always included; the option only removes parser diagnostics.

Errors

CheckError is returned only when the document was not checked at all:

  • DocumentTooDeep { depth, limit }: the parsed tree nests elements deeper than 512 levels. The validation layers walk the tree recursively; past that depth they would overflow a 2 MiB thread stack (the tokio worker default) and abort the process. Report it as “not run”, never as a pass: there are no findings, not even the parser’s, and the HTML itself may well be conforming. Browsers’ parsers stop nesting at the same depth (Blink appends further elements as siblings from level 512 on), so pages a browser renders as authored stay below it. The parser itself has no limit, as WHATWG tree construction has none.
  • Initialization { message }: the embedded HTML schema failed to load or the embedded rule set failed to parse.

A document that is merely non-conforming always produces Ok with findings. CheckError is #[non_exhaustive] and implements std::error::Error, so ? into Box<dyn Error> or anyhow::Error works.

Reading a report

  • Findings come in parser order, followed by the later layers: schema, assertions, scripts, CSP, table grid.
  • has_errors() is true if at least one finding has Severity::Error. Warnings, such as CSP violations or a missing lang attribute, do not count.
  • location is None when a finding cannot be mapped to a source range, for example a schema finding on an element the parser inserted implicitly (a <head> that is not in the source).
  • Match on rule_id rather than on message. Rule IDs are listed in Findings and rule IDs.

Patterns

Fail a test on invalid markup

#[test]
fn rendered_page_is_conforming() {
    let html = render_page(); // your templating
    let report = html_conform::check(&html).expect("checker starts");
    assert!(!report.has_errors(), "{:#?}", report.findings);
}

Only look at errors

use html_conform::Severity;

let errors: Vec<_> = report
    .findings
    .iter()
    .filter(|finding| finding.severity == Severity::Error)
    .collect();

Emit JSON

CheckReport, Finding, Severity and SourceLocation implement serde’s Serialize and Deserialize. With serde_json:

let json = serde_json::to_string_pretty(&report)?;

produces an object with a findings array. One entry, the last finding of the table grid sample in the showcase:

{
  "rule_id": "tables.integrity",
  "severity": "Error",
  "message": "Table cell overlaps an earlier table cell.",
  "location": {
    "line": 12,
    "column": 9,
    "byte_offset": 270
  }
}

This is the format of the examples/showcase/*.json files behind the die Beispiele des Crates, written by examples/findings.rs.

Edit this page on GitHub