Skip to main content

a11y_rules/
lib.rs

1//! Accessibility-Regeln, generisch über das Dokumentmodell.
2//!
3//! Ein Regelbestand, drei Oberflächen: Build-Zeit, CI/Crawl und die laufende
4//! Seite. Welche Regeln laufen können, hängt davon ab, welche
5//! [`Tier`](a11y_dom::Tier)s der Host bedient.
6//!
7//! # Nicht gelaufen ist nicht bestanden
8//!
9//! [`run`] läuft mit dem, was da ist, und hält für jede Regel fest, ob sie
10//! laufen konnte. Eine Tier-2-Regel auf einem Host ohne
11//! [`Semantics`](a11y_dom::Semantics) erzeugt keinen stillen Nicht-Befund,
12//! sondern einen Vermerk mit `NotRun::CapabilityMissing`.
13//!
14//! ```
15//! use a11y_dom::Arena;
16//! use a11y_rules::run;
17//!
18//! let doc = Arena::builder()
19//!     .open("html")
20//!         .open("body")
21//!             .open("img").attr("src", "logo.png").close()
22//!         .close()
23//!     .close()
24//!     .build();
25//!
26//! let report = run(&doc);
27//!
28//! // Gefunden: kein lang, kein title, kein alt.
29//! assert!(report.findings.iter().any(|f| f.rule_id == "images/alt-missing"));
30//! assert!(report.findings.iter().any(|f| f.rule_id == "document/lang-missing"));
31//!
32//! // Nicht beurteilt: die Tier-2-Regeln, weil dieser Host keine Semantik liefert.
33//! assert_eq!(report.summary.rules_not_run, 4);
34//! ```
35//!
36//! Mit einem Host, der [`Semantics`](a11y_dom::Semantics) erfüllt, laufen die
37//! über [`run_with_semantics`] mit.
38
39#![forbid(unsafe_code)]
40
41mod registry;
42mod semantics;
43mod structure;
44
45pub use registry::{Meta, SemanticsRule, StructureRule};
46
47use a11y_dom::{Document, Semantics};
48use a11y_report::{Finding, NotRun, Report, RuleRun};
49
50/// Alle Tier-1-Regeln.
51pub fn structure_rules<D: Document>() -> Vec<StructureRule<D>> {
52    structure::rules()
53}
54
55/// Alle Tier-2-Regeln.
56pub fn semantics_rules<D: Semantics>() -> Vec<SemanticsRule<D>> {
57    semantics::rules()
58}
59
60/// Die Deklarationen ohne Bindung an einen Host — für Werkzeuge, die den
61/// Regelbestand auflisten oder Kennungen benennen müssen, ohne ihn auszuführen.
62pub fn structure_metas() -> &'static [Meta] {
63    structure::METAS
64}
65
66/// Siehe [`structure_metas`].
67pub fn semantics_metas() -> &'static [Meta] {
68    semantics::METAS
69}
70
71/// Vermerkt je deklarierter Kennung, wie viele Befunde darauf entfallen.
72///
73/// Der Vermerk läuft über die **Befund**-Kennungen, nicht über eine
74/// übergeordnete Regelkennung. Nur so benutzen `rule_runs` und `findings`
75/// dieselbe Namensmenge und lassen sich verbinden.
76fn vermerke(meta: &Meta, gefunden: &[Finding], report: &mut Report) {
77    for id in meta.ids {
78        let anzahl = gefunden.iter().filter(|f| f.rule_id == *id).count();
79        report.record(RuleRun::ran(*id, anzahl));
80    }
81}
82
83fn run_structure<D: Document>(doc: &D, report: &mut Report) {
84    for rule in structure_rules::<D>() {
85        let mut out: Vec<Finding> = Vec::new();
86        (rule.run)(doc, &mut out);
87        vermerke(&rule.meta, &out, report);
88        report.extend(out);
89    }
90}
91
92/// Prüft ein Dokument, das nur Struktur liefert.
93///
94/// Tier-2-Regeln werden mit `NotRun::CapabilityMissing` vermerkt, nicht
95/// übergangen — der Bericht sagt damit aus, was er *nicht* geprüft hat.
96pub fn run<D: Document>(doc: &D) -> Report {
97    let mut report = Report::new();
98    run_structure(doc, &mut report);
99    for meta in semantics_metas() {
100        for id in meta.ids {
101            report.record(
102                RuleRun::not_run(*id, NotRun::CapabilityMissing)
103                    .with_reason("Host liefert keine Rolle und keinen Accessible Name"),
104            );
105        }
106    }
107    report.finish()
108}
109
110/// Prüft ein Dokument, das zusätzlich Rolle und Accessible Name liefert.
111pub fn run_with_semantics<D: Semantics>(doc: &D) -> Report {
112    let mut report = Report::new();
113    run_structure(doc, &mut report);
114    for rule in semantics_rules::<D>() {
115        let mut out: Vec<Finding> = Vec::new();
116        (rule.run)(doc, &mut out);
117        vermerke(&rule.meta, &out, &mut report);
118        report.extend(out);
119    }
120    report.finish()
121}