1use crate::element_ref::ElementRef;
4use crate::error::SelectorErrorKind;
5use cssparser::ToCss;
6use fast_html5ever::{LocalName, Namespace};
7use selectors::parser::SelectorParseErrorKind;
8use selectors::{matching, parser, NthIndexCache};
9use smallvec::SmallVec;
10use std::convert::TryFrom;
11use std::fmt;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Selector {
18 selectors: SmallVec<[parser::Selector<Simple>; 1]>,
20}
21
22impl Selector {
23 pub fn raw_query(&self) -> String {
25 self.selectors
26 .iter()
27 .filter_map(|s| {
28 let mut ss = String::new();
29 if s.to_css(&mut ss).is_ok() {
30 Some(ss)
31 } else {
32 None
33 }
34 })
35 .collect()
36 }
37
38 pub fn parse(selectors: &'_ str) -> Result<Self, SelectorErrorKind> {
40 let mut parser_input = cssparser::ParserInput::new(selectors);
41 let mut parser = cssparser::Parser::new(&mut parser_input);
42
43 parser::SelectorList::parse(&Parser, &mut parser, parser::ParseRelative::No)
44 .map(|list| Selector { selectors: list.0 })
45 .map_err(SelectorErrorKind::from)
46 }
47
48 pub fn matches(&self, element: &ElementRef) -> bool {
50 self.matches_with_scope(element, None)
51 }
52
53 pub fn matches_with_scope(&self, element: &ElementRef, scope: Option<ElementRef>) -> bool {
57 let mut binding = NthIndexCache::default();
58 let mut context = matching::MatchingContext::new(
59 matching::MatchingMode::Normal,
60 None,
61 &mut binding,
62 matching::QuirksMode::NoQuirks,
63 matching::NeedsSelectorFlags::No,
64 matching::IgnoreNthChildForInvalidation::No,
65 );
66 context.scope_element = scope.map(|x| selectors::Element::opaque(&x));
67 self.selectors
68 .iter()
69 .any(|s| matching::matches_selector(s, 0, None, element, &mut context))
70 }
71}
72
73struct Parser;
75impl<'i> parser::Parser<'i> for Parser {
76 type Impl = Simple;
77 type Error = SelectorParseErrorKind<'i>;
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct Simple;
83
84impl parser::SelectorImpl for Simple {
85 type AttrValue = CssString;
86 type Identifier = CssLocalName;
87 type LocalName = CssLocalName;
88 type NamespacePrefix = CssLocalName;
89 type NamespaceUrl = Namespace;
90 type BorrowedNamespaceUrl = Namespace;
91 type BorrowedLocalName = CssLocalName;
92
93 type NonTSPseudoClass = NonTSPseudoClass;
94 type PseudoElement = PseudoElement;
95
96 type ExtraMatchingData<'a> = std::marker::PhantomData<&'a ()>;
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
102pub struct CssString(pub String);
103
104impl<'a> From<&'a str> for CssString {
105 fn from(val: &'a str) -> Self {
106 Self(val.to_owned())
107 }
108}
109
110impl AsRef<str> for CssString {
111 fn as_ref(&self) -> &str {
112 &self.0
113 }
114}
115
116impl ToCss for CssString {
117 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
118 where
119 W: fmt::Write,
120 {
121 cssparser::serialize_string(&self.0, dest)
122 }
123}
124
125#[derive(Debug, Default, Clone, PartialEq, Eq)]
127pub struct CssLocalName(pub LocalName);
128
129impl<'a> From<&'a str> for CssLocalName {
130 fn from(val: &'a str) -> Self {
131 Self(val.into())
132 }
133}
134
135impl ToCss for CssLocalName {
136 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
137 where
138 W: fmt::Write,
139 {
140 dest.write_str(&self.0)
141 }
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146pub enum NonTSPseudoClass {}
147
148impl parser::NonTSPseudoClass for NonTSPseudoClass {
149 type Impl = Simple;
150
151 fn is_active_or_hover(&self) -> bool {
152 false
153 }
154
155 fn is_user_action_state(&self) -> bool {
156 false
157 }
158}
159
160impl ToCss for NonTSPseudoClass {
161 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
162 where
163 W: fmt::Write,
164 {
165 dest.write_str("")
166 }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub enum PseudoElement {}
172
173impl parser::PseudoElement for PseudoElement {
174 type Impl = Simple;
175}
176
177impl ToCss for PseudoElement {
178 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
179 where
180 W: fmt::Write,
181 {
182 dest.write_str("")
183 }
184}
185
186impl<'i> TryFrom<&'i str> for Selector {
187 type Error = SelectorErrorKind<'i>;
188
189 fn try_from(s: &'i str) -> Result<Self, Self::Error> {
190 Selector::parse(s)
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use std::convert::TryInto;
198
199 #[test]
200 fn selector_conversions() {
201 let s = "#testid.testclass";
202 let _sel: Selector = s.try_into().unwrap();
203
204 let s = s.to_owned();
205 let _sel: Selector = (*s).try_into().unwrap();
206 }
207
208 #[test]
209 #[should_panic]
210 fn invalid_selector_conversions() {
211 let s = "<failing selector>";
212 let _sel: Selector = s.try_into().unwrap();
213 }
214}