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