node_html_parser/css_select/helpers/
selectors.rs1use crate::css_select::types::{AttributeAction, AttributeSelector, InternalSelector, PseudoData};
2
3pub fn get_quality(token: &InternalSelector) -> i32 {
5 match token {
6 InternalSelector::Universal => 50,
7 InternalSelector::Tag { .. } => 30,
8 InternalSelector::Attribute(attr) => get_attribute_quality(attr),
9 InternalSelector::Pseudo { data, name } => match data {
10 PseudoData::None => 3,
11 PseudoData::SubSelectors(_) => {
12 if matches!(name.as_str(), "has" | "contains" | "icontains") { 0 } else { 2 }
13 }
14 PseudoData::Nth(_) => 3, }
16 _ => -1,
17 }
18}
19
20fn get_attribute_quality(attr: &AttributeSelector) -> i32 {
21 let base = match attr.action {
22 AttributeAction::Exists => 10,
23 AttributeAction::Equals => {
24 if attr.name == "id" {
25 9
26 } else {
27 8
28 }
29 }
30 AttributeAction::Not => 7,
31 AttributeAction::Start | AttributeAction::End => 6,
32 AttributeAction::Any => 5,
33 AttributeAction::Hyphen => 4,
34 AttributeAction::Element => 3,
35 };
36 if attr.ignore_case { base / 2 } else { base }
37}
38
39pub fn sort_rules(arr: &mut Vec<InternalSelector>) {
40 for i in 1..arr.len() {
42 let q_new = get_quality(&arr[i]);
43 if q_new < 0 {
44 continue;
45 }
46 let mut j = i;
47 while j > 0 {
48 let q_prev = get_quality(&arr[j - 1]);
49 if q_prev <= q_new {
50 break;
51 }
52 arr.swap(j, j - 1);
53 j -= 1;
54 }
55 }
56}
57
58pub fn is_traversal(token: &InternalSelector) -> bool {
59 matches!(
60 token,
61 InternalSelector::Descendant
62 | InternalSelector::Child
63 | InternalSelector::Sibling
64 | InternalSelector::Adjacent
65 | InternalSelector::FlexibleDescendant
66 )
67}
68
69pub fn includes_scope_pseudo(token: &InternalSelector) -> bool {
70 match token {
71 InternalSelector::Pseudo { name, data } => {
72 if name == "scope" { return true; }
73 match data {
74 PseudoData::SubSelectors(groups) => groups.iter().any(|g| g.iter().any(includes_scope_pseudo)),
75 _ => false,
76 }
77 }
78 _ => false,
79 }
80}