1 use std::{
2     collections::HashSet,
3     fmt::{self, Display, Write},
4     hash::{Hash, Hasher},
5     sync::atomic::{AtomicU32, Ordering as AtomicOrdering},
6 };
7 
8 use crate::error::SassResult;
9 
10 use super::{CompoundSelector, Pseudo, SelectorList, SimpleSelector, Specificity};
11 
12 pub(crate) static COMPLEX_SELECTOR_UNIQUE_ID: AtomicU32 = AtomicU32::new(0);
13 
14 #[derive(Clone, Debug)]
15 pub(crate) struct ComplexSelectorHashSet(HashSet<u32>);
16 
17 impl ComplexSelectorHashSet {
new() -> Self18     pub fn new() -> Self {
19         Self(HashSet::new())
20     }
21 
insert(&mut self, complex: &ComplexSelector) -> bool22     pub fn insert(&mut self, complex: &ComplexSelector) -> bool {
23         self.0.insert(complex.unique_id)
24     }
25 
contains(&self, complex: &ComplexSelector) -> bool26     pub fn contains(&self, complex: &ComplexSelector) -> bool {
27         self.0.contains(&complex.unique_id)
28     }
29 
extend<'a>(&mut self, complexes: impl Iterator<Item = &'a ComplexSelector>)30     pub fn extend<'a>(&mut self, complexes: impl Iterator<Item = &'a ComplexSelector>) {
31         self.0.extend(complexes.map(|complex| complex.unique_id));
32     }
33 }
34 
35 /// A complex selector.
36 ///
37 /// A complex selector is composed of `CompoundSelector`s separated by
38 /// `Combinator`s. It selects elements based on their parent selectors.
39 #[derive(Clone, Debug)]
40 pub(crate) struct ComplexSelector {
41     /// The components of this selector.
42     ///
43     /// This is never empty.
44     ///
45     /// Descendant combinators aren't explicitly represented here. If two
46     /// `CompoundSelector`s are adjacent to one another, there's an implicit
47     /// descendant combinator between them.
48     ///
49     /// It's possible for multiple `Combinator`s to be adjacent to one another.
50     /// This isn't valid CSS, but Sass supports it for CSS hack purposes.
51     pub components: Vec<ComplexSelectorComponent>,
52 
53     /// Whether a line break should be emitted *before* this selector.
54     pub line_break: bool,
55 
56     /// A unique identifier for this complex selector. Used to perform a pointer
57     /// equality check, like would be done for objects in a language like JavaScript
58     /// or dart
59     unique_id: u32,
60 }
61 
62 impl PartialEq for ComplexSelector {
eq(&self, other: &Self) -> bool63     fn eq(&self, other: &Self) -> bool {
64         self.components == other.components
65     }
66 }
67 
68 impl Eq for ComplexSelector {}
69 
70 impl Hash for ComplexSelector {
hash<H: Hasher>(&self, state: &mut H)71     fn hash<H: Hasher>(&self, state: &mut H) {
72         self.components.hash(state);
73     }
74 }
75 
76 impl fmt::Display for ComplexSelector {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result77     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78         let mut last_component = None;
79 
80         for component in &self.components {
81             if let Some(c) = last_component {
82                 if !omit_spaces_around(c) && !omit_spaces_around(component) {
83                     f.write_char(' ')?;
84                 }
85             }
86             write!(f, "{}", component)?;
87             last_component = Some(component);
88         }
89         Ok(())
90     }
91 }
92 
93 /// When `style` is `OutputStyle::compressed`, omit spaces around combinators.
omit_spaces_around(component: &ComplexSelectorComponent) -> bool94 fn omit_spaces_around(component: &ComplexSelectorComponent) -> bool {
95     // todo: compressed
96     let is_compressed = false;
97     is_compressed && matches!(component, ComplexSelectorComponent::Combinator(..))
98 }
99 
100 impl ComplexSelector {
new(components: Vec<ComplexSelectorComponent>, line_break: bool) -> Self101     pub fn new(components: Vec<ComplexSelectorComponent>, line_break: bool) -> Self {
102         Self {
103             components,
104             line_break,
105             unique_id: COMPLEX_SELECTOR_UNIQUE_ID.fetch_add(1, AtomicOrdering::Relaxed),
106         }
107     }
108 
max_specificity(&self) -> i32109     pub fn max_specificity(&self) -> i32 {
110         self.specificity().min
111     }
112 
min_specificity(&self) -> i32113     pub fn min_specificity(&self) -> i32 {
114         self.specificity().max
115     }
116 
specificity(&self) -> Specificity117     pub fn specificity(&self) -> Specificity {
118         let mut min = 0;
119         let mut max = 0;
120         for component in &self.components {
121             if let ComplexSelectorComponent::Compound(compound) = component {
122                 min += compound.min_specificity();
123                 max += compound.max_specificity();
124             }
125         }
126         Specificity::new(min, max)
127     }
128 
is_invisible(&self) -> bool129     pub fn is_invisible(&self) -> bool {
130         self.components
131             .iter()
132             .any(ComplexSelectorComponent::is_invisible)
133     }
134 
135     /// Returns whether `self` is a superselector of `other`.
136     ///
137     /// That is, whether `self` matches every element that `other` matches, as well
138     /// as possibly additional elements.
is_super_selector(&self, other: &Self) -> bool139     pub fn is_super_selector(&self, other: &Self) -> bool {
140         if let Some(ComplexSelectorComponent::Combinator(..)) = self.components.last() {
141             return false;
142         }
143         if let Some(ComplexSelectorComponent::Combinator(..)) = other.components.last() {
144             return false;
145         }
146 
147         let mut i1 = 0;
148         let mut i2 = 0;
149 
150         loop {
151             let remaining1 = self.components.len() - i1;
152             let remaining2 = other.components.len() - i2;
153 
154             if remaining1 == 0 || remaining2 == 0 || remaining1 > remaining2 {
155                 return false;
156             }
157 
158             let compound1 = match self.components.get(i1) {
159                 Some(ComplexSelectorComponent::Compound(c)) => c,
160                 Some(ComplexSelectorComponent::Combinator(..)) => return false,
161                 None => unreachable!(),
162             };
163 
164             if let ComplexSelectorComponent::Combinator(..) = other.components[i2] {
165                 return false;
166             }
167 
168             if remaining1 == 1 {
169                 let parents = other
170                     .components
171                     .iter()
172                     .take(other.components.len() - 1)
173                     .skip(i2)
174                     .cloned()
175                     .collect();
176                 return compound1.is_super_selector(
177                     other.components.last().unwrap().as_compound(),
178                     &Some(parents),
179                 );
180             }
181 
182             let mut after_super_selector = i2 + 1;
183             while after_super_selector < other.components.len() {
184                 if let Some(ComplexSelectorComponent::Compound(compound2)) =
185                     other.components.get(after_super_selector - 1)
186                 {
187                     if compound1.is_super_selector(
188                         compound2,
189                         &Some(
190                             other
191                                 .components
192                                 .iter()
193                                 .take(after_super_selector - 1)
194                                 .skip(i2 + 1)
195                                 .cloned()
196                                 .collect(),
197                         ),
198                     ) {
199                         break;
200                     }
201                 }
202 
203                 after_super_selector += 1;
204             }
205 
206             if after_super_selector == other.components.len() {
207                 return false;
208             }
209 
210             if let Some(ComplexSelectorComponent::Combinator(combinator1)) =
211                 self.components.get(i1 + 1)
212             {
213                 let combinator2 = match other.components.get(after_super_selector) {
214                     Some(ComplexSelectorComponent::Combinator(c)) => c,
215                     Some(ComplexSelectorComponent::Compound(..)) => return false,
216                     None => unreachable!(),
217                 };
218 
219                 if combinator1 == &Combinator::FollowingSibling {
220                     if combinator2 == &Combinator::Child {
221                         return false;
222                     }
223                 } else if combinator1 != combinator2 {
224                     return false;
225                 }
226 
227                 if remaining1 == 3 && remaining2 > 3 {
228                     return false;
229                 }
230 
231                 i1 += 2;
232                 i2 = after_super_selector + 1;
233             } else if let Some(ComplexSelectorComponent::Combinator(combinator2)) =
234                 other.components.get(after_super_selector)
235             {
236                 if combinator2 != &Combinator::Child {
237                     return false;
238                 }
239                 i1 += 1;
240                 i2 = after_super_selector + 1;
241             } else {
242                 i1 += 1;
243                 i2 = after_super_selector;
244             }
245         }
246     }
247 
contains_parent_selector(&self) -> bool248     pub fn contains_parent_selector(&self) -> bool {
249         self.components.iter().any(|c| {
250             if let ComplexSelectorComponent::Compound(compound) = c {
251                 compound.components.iter().any(|simple| {
252                     if simple.is_parent() {
253                         return true;
254                     }
255                     if let SimpleSelector::Pseudo(Pseudo {
256                         selector: Some(sel),
257                         ..
258                     }) = simple
259                     {
260                         return sel.contains_parent_selector();
261                     }
262                     false
263                 })
264             } else {
265                 false
266             }
267         })
268     }
269 }
270 
271 #[derive(Clone, Debug, Eq, PartialEq, Copy, Hash)]
272 pub(crate) enum Combinator {
273     /// Matches the right-hand selector if it's immediately adjacent to the
274     /// left-hand selector in the DOM tree.
275     ///
276     /// `'+'`
277     NextSibling,
278 
279     /// Matches the right-hand selector if it's a direct child of the left-hand
280     /// selector in the DOM tree.
281     ///
282     /// `'>'`
283     Child,
284 
285     /// Matches the right-hand selector if it comes after the left-hand selector
286     /// in the DOM tree.
287     ///
288     /// `'~'`
289     FollowingSibling,
290 }
291 
292 impl Display for Combinator {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result293     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294         f.write_char(match self {
295             Self::NextSibling => '+',
296             Self::Child => '>',
297             Self::FollowingSibling => '~',
298         })
299     }
300 }
301 
302 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
303 pub(crate) enum ComplexSelectorComponent {
304     Combinator(Combinator),
305     Compound(CompoundSelector),
306 }
307 
308 impl ComplexSelectorComponent {
is_invisible(&self) -> bool309     pub fn is_invisible(&self) -> bool {
310         match self {
311             Self::Combinator(..) => false,
312             Self::Compound(c) => c.is_invisible(),
313         }
314     }
315 
is_compound(&self) -> bool316     pub fn is_compound(&self) -> bool {
317         matches!(self, Self::Compound(..))
318     }
319 
is_combinator(&self) -> bool320     pub fn is_combinator(&self) -> bool {
321         matches!(self, Self::Combinator(..))
322     }
323 
resolve_parent_selectors( self, parent: SelectorList, ) -> SassResult<Option<Vec<ComplexSelector>>>324     pub fn resolve_parent_selectors(
325         self,
326         parent: SelectorList,
327     ) -> SassResult<Option<Vec<ComplexSelector>>> {
328         match self {
329             Self::Compound(c) => c.resolve_parent_selectors(parent),
330             Self::Combinator(..) => todo!(),
331         }
332     }
333 
as_compound(&self) -> &CompoundSelector334     pub fn as_compound(&self) -> &CompoundSelector {
335         match self {
336             Self::Compound(c) => c,
337             Self::Combinator(..) => unreachable!(),
338         }
339     }
340 }
341 
342 impl Display for ComplexSelectorComponent {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result343     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344         match self {
345             Self::Compound(c) => write!(f, "{}", c),
346             Self::Combinator(c) => write!(f, "{}", c),
347         }
348     }
349 }
350