accessibility_rs/engine/rules/utils/
nodes.rs

1use crate::engine::rules::rule::Validation;
2use accessibility_scraper::ElementRef;
3use accessibility_scraper::Selector;
4use selectors::Element;
5
6type ElementNodes<'a> = Vec<(ElementRef<'a>, Option<taffy::NodeId>)>;
7
8/// a valid alt attribute for image
9pub fn has_alt(ele: ElementRef<'_>) -> bool {
10    match ele.attr("role") {
11        Some(role) => {
12            if role == "presentation" {
13                return true;
14            }
15        }
16        _ => (),
17    };
18    has_alt_prop(ele)
19}
20
21/// a valid attribute for element
22pub fn has_alt_prop(ele: ElementRef<'_>) -> bool {
23    match ele.attr("alt") {
24        Some(_) => true,
25        _ => false,
26    }
27}
28
29/// property found for element
30pub fn has_prop(ele: ElementRef<'_>, prop: &str) -> bool {
31    match ele.attr(prop) {
32        Some(_) => true,
33        _ => false,
34    }
35}
36
37/// property found for element and is not empty
38pub fn has_prop_value(ele: ElementRef<'_>, prop: &str) -> bool {
39    match ele.attr(prop) {
40        Some(p) => !p.is_empty(),
41        _ => false,
42    }
43}
44
45/// elements empty
46pub fn is_empty(nodes: &ElementNodes) -> (bool, Vec<String>) {
47    let mut valid = true;
48    let mut elements = Vec::new();
49
50    for ele in nodes {
51        let ele = ele.0;
52        let empty = ele.inner_html().trim().is_empty();
53        if empty {
54            valid = false;
55            elements.push(get_unique_selector(&ele))
56        }
57    }
58
59    (valid, elements)
60}
61
62/// elements empty with validation
63pub fn validate_empty_nodes(nodes: &ElementNodes, id: &'static str) -> Validation {
64    let (valid, elements) = is_empty(&nodes);
65    Validation::new(valid, id, elements, Default::default())
66}
67
68/// check if the selector only exist for one element
69pub fn single_selector(ele: &ElementRef<'_>, node_selector: &str) -> bool {
70    match ele.tree().root().first_child() {
71        Some(child) => match ElementRef::wrap(child) {
72            Some(element) => match Selector::parse(node_selector) {
73                Ok(s) => {
74                    let e = element.select(&s);
75                    e.count() == 1
76                }
77                _ => false,
78            },
79            _ => false,
80        },
81        _ => false,
82    }
83}
84
85/// get the unique selector for an element
86pub fn get_unique_selector(ele: &ElementRef<'_>) -> String {
87    if ele.has_attribute("id") {
88        "#".to_string() + ele.attr("id").unwrap_or_default()
89    } else {
90        let mut selector = String::new();
91        let node_name = ele.value().name();
92
93        if node_name == "body" || node_name == "html" {
94            node_name.to_string()
95        } else {
96            let node_name = ele.value().name();
97
98            if selector.is_empty() && ele.has_attribute("class") {
99                let node_selector = node_name.to_string() + &ele.value().local_name.to_string();
100                let only_selector = single_selector(ele, &node_selector);
101                if only_selector {
102                    selector = node_selector;
103                }
104            }
105
106            if !selector.is_empty() {
107                selector
108            } else {
109                if single_selector(ele, &node_name) {
110                    node_name.to_string()
111                } else {
112                    let pos = get_sibling_position(ele);
113
114                    let s = match ele.parent_element() {
115                        Some(p) => {
116                            if selector.is_empty() {
117                                get_unique_selector(&p)
118                            } else {
119                                selector
120                            }
121                        }
122                        _ => ele.value().name().to_string(),
123                    };
124
125                    s + ">:nth-child(" + &pos.to_string() + ")"
126                }
127            }
128        }
129    }
130}
131
132/// get sibling position of element
133pub fn get_sibling_position(ele: &ElementRef<'_>) -> u8 {
134    let mut i = 1;
135
136    if ele.has_siblings() {
137        let mut sibling = ele.prev_sibling();
138
139        while let Some(e) = sibling {
140            i += 1;
141            sibling = e.prev_sibling();
142        }
143    }
144
145    i
146}
147
148/// validate missing attribute
149pub fn validate_missing_attr(
150    nodes: &ElementNodes,
151    attr: &'static str,
152    id: &'static str,
153) -> Validation {
154    let mut elements = Vec::new();
155    let mut valid = true;
156
157    nodes.iter().for_each(|e| {
158        if e.0.attr(attr).unwrap_or_default().is_empty() {
159            valid = false;
160            elements.push(get_unique_selector(&e.0))
161        }
162    });
163
164    Validation::new(valid, id, elements, Default::default())
165}