use super::RuleLookup;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WcagPrinciple {
Perceivable,
Operable,
Understandable,
Robust,
}
impl WcagPrinciple {
pub const ALL: [WcagPrinciple; 4] = [
WcagPrinciple::Perceivable,
WcagPrinciple::Operable,
WcagPrinciple::Understandable,
WcagPrinciple::Robust,
];
}
pub fn criterion_for_rule(rule_id: &str) -> Option<String> {
if is_wcag_criterion(rule_id) {
return Some(rule_id.to_string());
}
let rule = RuleLookup::by_legacy_wcag_id(rule_id).or_else(|| RuleLookup::by_id(rule_id))?;
rule.external_ref
.and_then(|r| r.strip_prefix("WCAG "))
.filter(|c| is_wcag_criterion(c))
.map(|c| c.to_string())
}
pub fn principle_for_criterion(criterion: &str) -> Option<WcagPrinciple> {
match criterion.split('.').next()?.parse::<u8>().ok()? {
1 => Some(WcagPrinciple::Perceivable),
2 => Some(WcagPrinciple::Operable),
3 => Some(WcagPrinciple::Understandable),
4 => Some(WcagPrinciple::Robust),
_ => None,
}
}
fn is_wcag_criterion(s: &str) -> bool {
let parts: Vec<&str> = s.split('.').collect();
parts.len() == 3
&& parts
.iter()
.all(|p| !p.is_empty() && p.chars().all(|c| c.is_ascii_digit()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn direct_wcag_id_is_its_own_criterion() {
assert_eq!(criterion_for_rule("1.4.3"), Some("1.4.3".to_string()));
assert_eq!(criterion_for_rule("4.1.2"), Some("4.1.2".to_string()));
}
#[test]
fn slug_rule_resolves_to_criterion() {
assert_eq!(
criterion_for_rule("aria-hidden-focus"),
Some("4.1.2".to_string())
);
}
#[test]
fn non_wcag_rule_returns_none() {
assert_eq!(criterion_for_rule("totally-unknown-rule"), None);
}
#[test]
fn principle_mapping() {
assert_eq!(
principle_for_criterion("1.1.1"),
Some(WcagPrinciple::Perceivable)
);
assert_eq!(
principle_for_criterion("2.4.7"),
Some(WcagPrinciple::Operable)
);
assert_eq!(
principle_for_criterion("3.1.1"),
Some(WcagPrinciple::Understandable)
);
assert_eq!(
principle_for_criterion("4.1.2"),
Some(WcagPrinciple::Robust)
);
}
}