Skip to content

Commit

Permalink
chore(selector): add fast selector pathing
Browse files Browse the repository at this point in the history
  • Loading branch information
j-mendez committed Oct 14, 2023
1 parent e06c9df commit b55de68
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 121 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion accessibility-rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "accessibility-rs"
version = "0.0.20"
version = "0.0.21"
authors = ["The A11yWatch Project Developers", "Jeff Mendez <[email protected]>"]
edition = "2021"
license = "MIT OR Apache-2.0"
Expand Down
2 changes: 2 additions & 0 deletions accessibility-rs/src/engine/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
pub mod rule;
/// the techniques to adhere principles and guidelines
pub mod techniques;
/// utilities to help rules.
pub mod utils;
/// the base of the wcag set
pub mod wcag_base;
/// rules to map to
Expand Down
1 change: 1 addition & 0 deletions accessibility-rs/src/engine/rules/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod nodes;
135 changes: 135 additions & 0 deletions accessibility-rs/src/engine/rules/utils/nodes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use crate::engine::rules::rule::Validation;
use crate::ElementRef;
use accessibility_scraper::Selector;
use selectors::Element;
use slotmap::DefaultKey;

type ElementNodes<'a> = Vec<(ElementRef<'a>, Option<DefaultKey>)>;

/// a valid alt attribute for image
pub fn has_alt(ele: ElementRef<'_>) -> bool {
let mut valid = true;
match ele.attr("role") {
Some(role) => {
if role == "presentation" {
return valid;
}
}
_ => (),
};
match ele.attr("alt") {
Some(_) => (),
_ => valid = false,
}
valid
}

/// elements empty
pub fn is_empty(nodes: &ElementNodes) -> bool {
let mut empty = false;
for ele in nodes {
let ele = ele.0;
empty = ele.inner_html().trim().is_empty();
}
empty
}

/// check if the selector only exist for one element
pub fn single_selector(ele: &ElementRef<'_>, node_selector: &str) -> bool {
match ele.tree().root().first_child() {
Some(child) => match ElementRef::wrap(child) {
Some(element) => match Selector::parse(node_selector) {
Ok(s) => {
let e = element.select(&s);
e.count() == 1
}
_ => false,
},
_ => false,
},
_ => false,
}
}

/// get the unique selector for an element
pub fn get_unique_selector(ele: &ElementRef<'_>, first_sweep: bool) -> String {
if ele.has_attribute("id") {
"#".to_string() + ele.attr("id").unwrap_or_default()
} else {
let mut selector = String::new();
let node_name = ele.value().name();

if node_name == "body" || node_name == "html" {
node_name.to_string()
} else {
let node_name = ele.value().name();

if selector.is_empty() && ele.has_attribute("class") {
let node_selector = node_name.to_string() + &ele.local_name().to_string();
let only_selector = single_selector(ele, &node_selector);
if only_selector {
selector = node_selector;
}
}

if first_sweep && !selector.is_empty() {
selector
} else {
if first_sweep && single_selector(ele, &node_name) {
node_name.to_string()
} else {
let pos = get_sibling_position(ele);

let s = match ele.parent_element() {
Some(p) => {
if selector.is_empty() {
// todo: if selector is unique along chain run first
get_unique_selector(&p, false)
} else {
selector
}
}
_ => ele.value().name().to_string(),
};

s + ">:nth-child(" + &pos.to_string() + ")"
}
}
}
}
}

/// get sibling position of element
pub fn get_sibling_position(ele: &ElementRef<'_>) -> u8 {
let mut i = 1;

if ele.has_siblings() {
let mut sibling = ele.prev_sibling();

while let Some(e) = sibling {
i += 1;
sibling = e.prev_sibling();
}
}

i
}

/// validate missing attribute
pub fn validate_missing_attr(
nodes: &ElementNodes,
attr: &'static str,
id: &'static str,
) -> Validation {
let mut elements = Vec::new();
let mut valid = true;

nodes.iter().for_each(|e| {
if e.0.attr(attr).unwrap_or_default().is_empty() {
valid = false;
elements.push(get_unique_selector(&e.0, true))
}
});

Validation::new(valid, id, elements, "")
}
Loading

0 comments on commit b55de68

Please sign in to comment.