pochoir_parser/tree/
selection.rs1use cssparser::ToCss;
2use pochoir_template_engine::TemplateBlock;
3use precomputed_hash::PrecomputedHash;
4use selectors::{
5 matching,
6 parser::{self, ParseRelative, SelectorList, SelectorParseErrorKind},
7 Element, OpaqueElement,
8};
9use std::{
10 fmt,
11 hash::{DefaultHasher, Hash, Hasher},
12};
13
14use super::{TreeRef, TreeRefId};
15use crate::Node;
16
17pub fn build_selector_list(
27 selector: &str,
28) -> Result<SelectorList<InnerSelector>, cssparser::ParseError<'_, SelectorParseErrorKind<'_>>> {
29 let mut input = cssparser::ParserInput::new(selector);
30 let mut parser = cssparser::Parser::new(&mut input);
31 SelectorList::parse(&InnerSelectorParser, &mut parser, ParseRelative::No)
32}
33
34impl Element for TreeRef<'_, '_> {
35 type Impl = InnerSelector;
36
37 fn opaque(&self) -> OpaqueElement {
38 OpaqueElement::new(&self.id)
39 }
40
41 fn parent_element(&self) -> Option<Self> {
42 let parent = self.parent();
43
44 if parent == *self {
45 None
46 } else {
47 Some(parent)
48 }
49 }
50
51 fn parent_node_is_shadow_root(&self) -> bool {
52 false
53 }
54
55 fn containing_shadow_host(&self) -> Option<Self> {
56 None
57 }
58
59 fn is_pseudo_element(&self) -> bool {
60 false
61 }
62
63 fn prev_sibling_element(&self) -> Option<Self> {
64 self.prev_sibling()
65 }
66
67 fn next_sibling_element(&self) -> Option<Self> {
68 self.next_sibling()
69 }
70
71 fn first_element_child(&self) -> Option<Self> {
72 self.children().next()
73 }
74
75 fn is_html_element_in_html_document(&self) -> bool {
76 true
77 }
78
79 fn has_local_name(
80 &self,
81 local_name: &<Self::Impl as selectors::SelectorImpl>::BorrowedLocalName,
82 ) -> bool {
83 match self.data() {
84 Node::Element(ref name, _) => *name == local_name.inner,
85 _ => false,
86 }
87 }
88
89 fn has_namespace(
90 &self,
91 _ns: &<Self::Impl as selectors::SelectorImpl>::BorrowedNamespaceUrl,
92 ) -> bool {
93 true
94 }
95
96 fn is_same_type(&self, other: &Self) -> bool {
97 if let Node::Element(ref name, _) = self.data() {
98 let name = name.clone();
99
100 if let Node::Element(ref other_name, _) = other.data() {
101 return name == *other_name;
102 }
103 }
104
105 false
106 }
107
108 fn attr_matches(
109 &self,
110 _ns: &selectors::attr::NamespaceConstraint<
111 &<Self::Impl as selectors::SelectorImpl>::NamespaceUrl,
112 >,
113 local_name: &<Self::Impl as selectors::SelectorImpl>::LocalName,
114 operation: &selectors::attr::AttrSelectorOperation<
115 &<Self::Impl as selectors::SelectorImpl>::AttrValue,
116 >,
117 ) -> bool {
118 match self.data() {
119 Node::Element(_, ref attrs) => attrs.iter().any(|(key, val)| {
120 *local_name == LocalName::from(&***key)
122 && operation.eval_str(
123 &val.iter()
124 .filter_map(|b| {
125 if let TemplateBlock::RawText(t) = &**b {
126 Some(t.to_string())
127 } else {
128 None
129 }
130 })
131 .collect::<String>(),
132 )
133 }),
134 _ => false,
135 }
136 }
137
138 fn match_non_ts_pseudo_class(
139 &self,
140 _pc: &<Self::Impl as selectors::SelectorImpl>::NonTSPseudoClass,
141 _context: &mut matching::MatchingContext<Self::Impl>,
142 ) -> bool {
143 false
144 }
145
146 fn match_pseudo_element(
147 &self,
148 _pe: &<Self::Impl as selectors::SelectorImpl>::PseudoElement,
149 _context: &mut matching::MatchingContext<Self::Impl>,
150 ) -> bool {
151 false
152 }
153
154 fn apply_selector_flags(&self, _flags: matching::ElementSelectorFlags) {}
155
156 fn is_link(&self) -> bool {
157 matches!(self.data(), Node::Element(ref el_name, _) if el_name == "link")
158 }
159
160 fn is_html_slot_element(&self) -> bool {
161 true
162 }
163
164 fn has_id(
165 &self,
166 id: &<Self::Impl as selectors::SelectorImpl>::Identifier,
167 case_sensitivity: selectors::attr::CaseSensitivity,
168 ) -> bool {
169 if let Node::Element(_, _) = self.data() {
170 self.attr("id")
171 .unwrap_or_default()
172 .is_some_and(|v| case_sensitivity.eq(v.as_bytes(), id.inner.as_bytes()))
173 } else {
174 false
175 }
176 }
177
178 fn has_class(
179 &self,
180 name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
181 case_sensitivity: selectors::attr::CaseSensitivity,
182 ) -> bool {
183 if let Node::Element(_, _) = self.data() {
184 self.attr("class")
185 .unwrap_or_default()
186 .is_some_and(|v| case_sensitivity.eq(v.as_bytes(), name.inner.as_bytes()))
187 } else {
188 false
189 }
190 }
191
192 fn imported_part(
193 &self,
194 _name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
195 ) -> Option<<Self::Impl as selectors::SelectorImpl>::Identifier> {
196 None
197 }
198
199 fn is_part(&self, _name: &<Self::Impl as selectors::SelectorImpl>::Identifier) -> bool {
200 false
201 }
202
203 fn is_empty(&self) -> bool {
204 !self
205 .children()
206 .any(|c| matches!(c.data(), Node::Element(_, _) | Node::TemplateBlock(_)))
207 }
208
209 fn is_root(&self) -> bool {
210 self.id == TreeRefId::Root
211 }
212
213 fn assigned_slot(&self) -> Option<Self> {
214 None
215 }
216
217 fn ignores_nth_child_selectors(&self) -> bool {
218 false
219 }
220
221 fn has_custom_state(&self, _name: &<Self::Impl as parser::SelectorImpl>::Identifier) -> bool {
222 false
223 }
224
225 fn add_element_unique_hashes(&self, _filter: &mut selectors::bloom::BloomFilter) -> bool {
226 false
227 }
228}
229
230#[derive(Debug, Clone, PartialEq, Eq, Default)]
231pub struct LocalName {
232 inner: String,
233}
234
235impl AsRef<str> for LocalName {
236 fn as_ref(&self) -> &str {
237 self.inner.as_str()
238 }
239}
240
241impl From<&str> for LocalName {
242 fn from(v: &str) -> Self {
243 Self {
244 inner: v.to_string(),
245 }
246 }
247}
248
249impl PrecomputedHash for LocalName {
250 fn precomputed_hash(&self) -> u32 {
251 let mut hasher = DefaultHasher::new();
252 self.inner.hash(&mut hasher);
253 hasher.finish() as u32
254 }
255}
256
257impl ToCss for LocalName {
258 fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
259 dest.write_str(&self.inner)
260 }
261}
262
263#[derive(Debug, Clone, PartialEq, Eq, Default)]
264pub struct Namespace;
265
266impl PrecomputedHash for Namespace {
267 fn precomputed_hash(&self) -> u32 {
268 0
269 }
270}
271
272pub struct InnerSelectorParser;
273
274impl<'i> parser::Parser<'i> for InnerSelectorParser {
275 type Impl = InnerSelector;
276 type Error = SelectorParseErrorKind<'i>;
277}
278
279#[derive(Debug, Clone)]
280pub struct InnerSelector;
281
282impl parser::SelectorImpl for InnerSelector {
283 type ExtraMatchingData<'a> = String;
284 type AttrValue = LocalName;
285 type Identifier = LocalName;
286 type LocalName = LocalName;
287 type NamespaceUrl = Namespace;
288 type NamespacePrefix = LocalName;
289 type BorrowedLocalName = LocalName;
290 type BorrowedNamespaceUrl = Namespace;
291
292 type NonTSPseudoClass = NonTSPseudoClass;
293 type PseudoElement = PseudoElement;
294}
295
296#[derive(Debug, Clone, Eq, PartialEq)]
297pub struct NonTSPseudoClass;
298
299impl parser::NonTSPseudoClass for NonTSPseudoClass {
300 type Impl = InnerSelector;
301
302 fn is_active_or_hover(&self) -> bool {
303 false
304 }
305
306 fn is_user_action_state(&self) -> bool {
307 false
308 }
309}
310
311impl ToCss for NonTSPseudoClass {
312 fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
313 dest.write_str("")
314 }
315}
316
317#[derive(Debug, Clone, Eq, PartialEq)]
318pub struct PseudoElement;
319
320impl parser::PseudoElement for PseudoElement {
321 type Impl = InnerSelector;
322}
323
324impl ToCss for PseudoElement {
325 fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
326 dest.write_str("")
327 }
328}