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