orbtk_css_engine/
selector.rs

1use std::fmt;
2use std::{collections::HashSet, ops::Add};
3
4#[derive(Clone, Debug)]
5pub enum SelectorRelation {
6    Ancestor(Selector),
7    Parent(Selector),
8}
9
10/// Describes the specificity of a selector.
11///
12/// The indexes are as follows:
13/// 0 - number of IDs (most important)
14/// 1 - number of classes and pseudo-classes
15/// 2 - number of elements (least important)
16#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
17pub struct Specificity([u8; 3]);
18
19impl Add<Self> for Specificity {
20    type Output = Self;
21
22    fn add(self, rhs: Self) -> Self::Output {
23        Specificity([
24            self.0[0] + rhs.0[0],
25            self.0[1] + rhs.0[1],
26            self.0[2] + rhs.0[2],
27        ])
28    }
29}
30
31/// Describes a css selector.
32#[derive(Debug, Default)]
33pub struct Selector {
34    pub id: Option<String>,
35    pub element: Option<String>,
36    pub classes: HashSet<String>,
37    pub pseudo_classes: HashSet<String>,
38    pub relation: Option<Box<SelectorRelation>>,
39    pub dirty: bool,
40}
41
42/// Inner selector value.
43impl Selector {
44    pub fn new() -> Self {
45        Selector {
46            id: None,
47            element: None,
48            classes: HashSet::new(),
49            pseudo_classes: HashSet::new(),
50            relation: None,
51            dirty: true,
52        }
53    }
54
55    pub fn is_empty(&self) -> bool {
56        self.element.is_none()
57            && self.id.is_none()
58            && self.classes.is_empty()
59            && self.pseudo_classes.is_empty()
60    }
61
62    pub fn dirty(&self) -> bool {
63        self.dirty
64    }
65
66    pub fn set_dirty(&mut self, dirty: bool) {
67        self.dirty = dirty;
68    }
69
70    pub fn specificity(&self) -> Specificity {
71        let s = Specificity([
72            if self.id.is_some() { 1 } else { 0 },
73            (self.classes.len() + self.pseudo_classes.len()) as u8,
74            if self.element.is_some() { 1 } else { 0 },
75        ]);
76
77        if let Some(ref relation) = self.relation {
78            match **relation {
79                SelectorRelation::Ancestor(ref x) | SelectorRelation::Parent(ref x) => {
80                    return x.specificity() + s;
81                }
82            }
83        }
84
85        s
86    }
87
88    pub fn matches(&self, other: &Selector) -> bool {
89        if self.id.is_some() && self.id != other.id {
90            return false;
91        }
92
93        if self.element.is_some() && self.element != other.element {
94            return false;
95        }
96
97        if !other.classes.is_superset(&self.classes) {
98            return false;
99        }
100
101        if !other.pseudo_classes.is_superset(&self.pseudo_classes) {
102            return false;
103        }
104
105        true
106    }
107
108    pub fn with<S: Into<String>>(mut self, element: S) -> Self {
109        self.element = Some(element.into());
110        self
111    }
112
113    pub fn id<S: Into<String>>(mut self, id: S) -> Self {
114        self.id = Some(id.into());
115        self
116    }
117
118    pub fn class<S: Into<String>>(mut self, class: S) -> Self {
119        self.classes.insert(class.into());
120        self
121    }
122
123    pub fn without_class<S: Into<String>>(mut self, class: S) -> Self {
124        self.classes.remove(&class.into());
125        self
126    }
127
128    pub fn pseudo_class<S: Into<String>>(mut self, pseudo_class: S) -> Self {
129        self.pseudo_classes.insert(pseudo_class.into());
130        self
131    }
132
133    pub fn without_pseudo_class<S: Into<String>>(mut self, pseudo_class: S) -> Self {
134        self.pseudo_classes.remove(&pseudo_class.into());
135        self
136    }
137}
138
139impl PartialEq for Selector {
140    fn eq(&self, other: &Selector) -> bool {
141        self.id == other.id
142    }
143}
144
145impl Clone for Selector {
146    fn clone(&self) -> Self {
147        Selector {
148            id: self.id.clone(),
149            element: self.element.clone(),
150            classes: self.classes.clone(),
151            pseudo_classes: self.pseudo_classes.clone(),
152            relation: self.relation.clone(),
153            dirty: self.dirty,
154        }
155    }
156}
157
158impl fmt::Display for Selector {
159    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
160        if let Some(element) = &self.element {
161            return write!(f, ", css: {}", element);
162        }
163
164        write!(f, "")
165    }
166}
167
168// --- Conversions ---
169
170impl From<String> for Selector {
171    fn from(s: String) -> Selector {
172        Selector::new().with(s)
173    }
174}
175
176impl From<&str> for Selector {
177    fn from(s: &str) -> Selector {
178        Selector::new().with(s.to_string())
179    }
180}
181
182impl From<&Selector> for Selector {
183    fn from(s: &Selector) -> Selector {
184        let mut selector = Selector::default();
185        selector.element = s.element.clone();
186        selector.id = s.id.clone();
187        selector.classes = s.classes.clone();
188        selector.pseudo_classes = s.pseudo_classes.clone();
189        selector
190    }
191}