node_html_parser/css_select/
convert.rs

1use crate::css_select::legacy::{AttrOp, Combinator, CompoundSelector, Pseudo, Selector};
2use crate::css_select::types::{AttributeAction, AttributeSelector, InternalSelector, PseudoData};
3
4// 修复点:
5// 1. 生成顺序与 JS 版本保持一致 (左->右:左侧 simple tokens, combinator, 右侧 simple tokens)。
6// 2. 将 :not(...) 的内部 selector 转成 data,避免信息丢失;其它函数伪类暂保持 data=None。
7
8fn simple_pseudo(name: &str) -> InternalSelector {
9	InternalSelector::Pseudo {
10		name: name.into(),
11		data: PseudoData::None,
12	}
13}
14
15// 转换单个复合选择器 (不含连接符)
16fn compound_to_internal(comp: &CompoundSelector) -> Vec<InternalSelector> {
17	let mut out = Vec::new();
18	if let Some(tag) = &comp.tag {
19		out.push(InternalSelector::Tag { name: tag.clone() });
20	}
21	if let Some(id) = &comp.id {
22		out.push(InternalSelector::Attribute(AttributeSelector {
23			name: "id".into(),
24			action: AttributeAction::Equals,
25			value: Some(id.clone()),
26			ignore_case: false,
27		}));
28	}
29	for cls in &comp.classes {
30		out.push(InternalSelector::Attribute(AttributeSelector {
31			name: "class".into(),
32			action: AttributeAction::Element,
33			value: Some(cls.clone()),
34			ignore_case: false,
35		}));
36	}
37	for a in &comp.attrs {
38		let action = match a.op {
39			AttrOp::Exists => AttributeAction::Exists,
40			AttrOp::Eq => AttributeAction::Equals,
41			AttrOp::Prefix => AttributeAction::Start,
42			AttrOp::Suffix => AttributeAction::End,
43			AttrOp::Substr => AttributeAction::Any,
44			AttrOp::Includes => AttributeAction::Element,
45			AttrOp::Dash => AttributeAction::Hyphen,
46		};
47		out.push(InternalSelector::Attribute(AttributeSelector {
48			name: a.name.clone(),
49			action,
50			value: if matches!(a.op, AttrOp::Exists) {
51				None
52			} else {
53				Some(a.value.clone())
54			},
55			ignore_case: matches!(a.case, crate::css_select::legacy::CaseMode::Insensitive),
56		}));
57	}
58	for p in &comp.pseudos {
59		match p {
60			Pseudo::Not(list) => {
61				let mut groups: Vec<Vec<InternalSelector>> = Vec::new();
62				for sel in list {
63					for seq in selector_to_internal(sel) {
64						groups.push(seq);
65					}
66				}
67				out.push(InternalSelector::Pseudo {
68					name: "not".into(),
69					data: PseudoData::SubSelectors(groups),
70				});
71			}
72			Pseudo::FirstChild => out.push(simple_pseudo("first-child")),
73			Pseudo::LastChild => out.push(simple_pseudo("last-child")),
74			Pseudo::OnlyChild => out.push(simple_pseudo("only-child")),
75			Pseudo::FirstOfType => out.push(simple_pseudo("first-of-type")),
76			Pseudo::LastOfType => out.push(simple_pseudo("last-of-type")),
77			Pseudo::OnlyOfType => out.push(simple_pseudo("only-of-type")),
78			Pseudo::NthChild(expr) => out.push(InternalSelector::Pseudo {
79				name: "nth-child".into(),
80				data: PseudoData::Nth(expr.clone()),
81			}),
82			Pseudo::NthLastChild(expr) => out.push(InternalSelector::Pseudo {
83				name: "nth-last-child".into(),
84				data: PseudoData::Nth(expr.clone()),
85			}),
86			Pseudo::NthOfType(expr) => out.push(InternalSelector::Pseudo {
87				name: "nth-of-type".into(),
88				data: PseudoData::Nth(expr.clone()),
89			}),
90			Pseudo::NthLastOfType(expr) => out.push(InternalSelector::Pseudo {
91				name: "nth-last-of-type".into(),
92				data: PseudoData::Nth(expr.clone()),
93			}),
94			Pseudo::Is(list) => {
95				let mut groups = Vec::new();
96				for sel in list {
97					for seq in selector_to_internal(sel) {
98						groups.push(seq);
99					}
100				}
101				out.push(InternalSelector::Pseudo {
102					name: "is".into(),
103					data: PseudoData::SubSelectors(groups),
104				});
105			}
106			Pseudo::Where(list) => {
107				let mut groups = Vec::new();
108				for sel in list {
109					for seq in selector_to_internal(sel) {
110						groups.push(seq);
111					}
112				}
113				out.push(InternalSelector::Pseudo {
114					name: "where".into(),
115					data: PseudoData::SubSelectors(groups),
116				});
117			}
118			Pseudo::Has(list) => {
119				let mut groups = Vec::new();
120				for sel in list {
121					for seq in selector_to_internal(sel) {
122						groups.push(seq);
123					}
124				}
125				out.push(InternalSelector::Pseudo {
126					name: "has".into(),
127					data: PseudoData::SubSelectors(groups),
128				});
129			}
130			Pseudo::Empty => out.push(simple_pseudo("empty")),
131			Pseudo::Root => out.push(simple_pseudo("root")),
132			Pseudo::Scope => out.push(simple_pseudo("scope")),
133		}
134	}
135	out
136}
137
138pub fn selector_to_internal(sel: &Selector) -> Vec<Vec<InternalSelector>> {
139	let mut seq: Vec<InternalSelector> = Vec::new();
140	let parts = &sel.0;
141	if parts.is_empty() {
142		return vec![seq];
143	}
144	for (idx, (_comb, comp)) in parts.iter().enumerate() {
145		let mut simple_tokens = compound_to_internal(comp);
146		seq.append(&mut simple_tokens);
147		if let Some((Some(next_comb), _)) = parts.get(idx + 1) {
148			let comb_token = match next_comb {
149				Combinator::Descendant => InternalSelector::Descendant,
150				Combinator::Child => InternalSelector::Child,
151				Combinator::Adjacent => InternalSelector::Adjacent,
152				Combinator::Sibling => InternalSelector::Sibling,
153			};
154			seq.push(comb_token);
155		}
156	}
157	vec![seq]
158}