pochoir_parser/tree/
selection.rs

1use 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
17/// Parse and build a [`SelectorList`] from an arbitrary string.
18///
19/// It is used in functions selecting elements like [`TreeRef::select`].
20///
21/// # Errors
22///
23/// Returns an error if the CSS selector is malformed.
24///
25/// [`SelectorList`]: selectors::parser::SelectorList
26pub 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                // Ignore template blocks
121                *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}