use std::sync::OnceLock;
use crate::taxonomy::rules::RULES;
pub const WCAG_AA_TOTAL: usize = 50;
const WCAG_22_ONLY_CRITERIA: &[&str] = &[
"2.4.11", "2.4.12", "2.4.13", "2.5.7", "2.5.8", "3.2.6", "3.3.7", "3.3.8", "3.3.9",
];
fn wcag_id_order(id: &str) -> Vec<u32> {
id.split('.').filter_map(|p| p.parse().ok()).collect()
}
pub fn automated_criteria() -> &'static [(&'static str, &'static str)] {
static CACHE: OnceLock<Vec<(&'static str, &'static str)>> = OnceLock::new();
CACHE.get_or_init(|| {
let mut out: Vec<(&'static str, &'static str)> = Vec::new();
for rule in RULES {
let (Some(ext_ref), Some(level)) = (rule.external_ref, rule.external_level) else {
continue;
};
let Some(id) = ext_ref.strip_prefix("WCAG ") else {
continue;
};
if !out.iter().any(|(existing, _)| *existing == id) {
out.push((id, level));
}
}
out.sort_by_key(|a| wcag_id_order(a.0));
out
})
}
const MANUAL_REVIEW_CRITERIA_RAW: &[(&str, &str, &str)] = &[
("1.2.1", "A", "Audio-only and Video-only (Prerecorded)"),
("1.2.2", "A", "Captions (Prerecorded)"),
("1.2.3", "A", "Audio Description or Media Alternative"),
("1.2.5", "AA", "Audio Description (Prerecorded)"),
("1.4.2", "A", "Audio Control"),
("1.4.5", "AA", "Images of Text"),
("2.1.4", "A", "Character Key Shortcuts"),
("2.2.2", "A", "Pause, Stop, Hide"),
("2.3.1", "A", "Three Flashes or Below Threshold"),
("2.5.1", "A", "Pointer Gestures"),
("2.5.2", "A", "Pointer Cancellation"),
("2.5.4", "A", "Motion Actuation"),
("3.2.3", "AA", "Consistent Navigation"),
("3.2.4", "AA", "Consistent Identification"),
("3.3.3", "AA", "Error Suggestion"),
("3.3.4", "AA", "Error Prevention (Legal, Financial, Data)"),
];
pub fn manual_review_criteria() -> &'static [(&'static str, &'static str, &'static str)] {
static CACHE: OnceLock<Vec<(&'static str, &'static str, &'static str)>> = OnceLock::new();
CACHE.get_or_init(|| {
let automated = automated_criteria();
MANUAL_REVIEW_CRITERIA_RAW
.iter()
.filter(|(id, _, _)| !automated.iter().any(|(aid, _)| aid == id))
.copied()
.collect()
})
}
pub fn coverage_stats() -> (usize, usize) {
let aa_count = automated_criteria()
.iter()
.filter(|(id, l)| (*l == "A" || *l == "AA") && !WCAG_22_ONLY_CRITERIA.contains(id))
.count();
(aa_count, WCAG_AA_TOTAL)
}