cadmus_core/document/html/
css.rs

1use super::style::StyleSheet;
2use fxhash::FxHashSet;
3
4#[derive(Debug, Clone)]
5pub struct Selector {
6    pub simple_selectors: Vec<SimpleSelector>,
7    pub combinators: Vec<Combinator>,
8}
9
10#[derive(Debug, Clone)]
11pub struct SimpleSelector {
12    pub tag_name: Option<String>,
13    pub classes: FxHashSet<String>,
14    pub pseudo_classes: Vec<PseudoClass>,
15    pub attributes: Vec<Attribute>,
16    pub id: Option<String>,
17}
18
19#[derive(Debug, Clone)]
20pub struct Attribute {
21    pub name: String,
22    pub operator: AttributeOperator,
23}
24
25#[derive(Debug, Clone)]
26pub enum PseudoClass {
27    FirstChild,
28    LastChild,
29}
30
31#[derive(Debug, Clone)]
32pub enum AttributeOperator {
33    // `[attr]`
34    Exists,
35    // `[attr=value]`
36    Matches(String),
37    // `[attr~=value]`
38    Contains(String),
39    // `[attr|=value]`
40    StartsWith(String),
41}
42
43#[derive(Debug, Copy, Clone)]
44pub enum Combinator {
45    None,
46    // a > b
47    Child,
48    // a   b
49    Descendant,
50    // a + b
51    NextSibling,
52    // a ~ b
53    SubsequentSibling,
54}
55
56impl Default for Selector {
57    fn default() -> Self {
58        Selector {
59            simple_selectors: Vec::new(),
60            combinators: Vec::new(),
61        }
62    }
63}
64
65impl Default for SimpleSelector {
66    fn default() -> Self {
67        SimpleSelector {
68            tag_name: None,
69            classes: FxHashSet::default(),
70            pseudo_classes: Vec::new(),
71            attributes: Vec::new(),
72            id: None,
73        }
74    }
75}
76
77pub type Specificity = [usize; 3];
78
79impl Selector {
80    pub fn specificity(&self) -> Specificity {
81        let mut spec = [0usize; 3];
82        for sel in &self.simple_selectors {
83            spec[0] = spec[0].saturating_add(sel.id.iter().count());
84            spec[1] = spec[1].saturating_add(sel.classes.len());
85            spec[1] = spec[1].saturating_add(sel.pseudo_classes.len());
86            spec[1] = spec[1].saturating_add(sel.attributes.len());
87            spec[2] = spec[2].saturating_add(sel.tag_name.iter().count());
88        }
89        spec
90    }
91}
92
93#[derive(Debug, Clone)]
94pub struct Declaration {
95    pub name: String,
96    pub value: String,
97    pub important: bool,
98}
99
100impl Default for Declaration {
101    fn default() -> Declaration {
102        Declaration {
103            name: String::default(),
104            value: String::default(),
105            important: false,
106        }
107    }
108}
109
110#[derive(Debug, Clone)]
111pub struct Rule {
112    pub selector: Selector,
113    pub declarations: Vec<Declaration>,
114}
115
116#[derive(Debug)]
117pub struct CssParser<'a> {
118    input: &'a str,
119    offset: usize,
120}
121
122impl<'a> CssParser<'a> {
123    pub fn new(input: &str) -> CssParser<'_> {
124        CssParser { input, offset: 0 }
125    }
126
127    fn eof(&self) -> bool {
128        self.offset >= self.input.len()
129    }
130
131    fn next(&self) -> Option<char> {
132        self.input[self.offset..].chars().next()
133    }
134
135    fn starts_with(&self, s: &str) -> bool {
136        self.input[self.offset..].starts_with(s)
137    }
138
139    fn advance(&mut self, n: usize) {
140        for c in self.input[self.offset..].chars().take(n) {
141            self.offset += c.len_utf8();
142        }
143    }
144
145    fn advance_while<F>(&mut self, test: F)
146    where
147        F: FnMut(&char) -> bool,
148    {
149        for c in self.input[self.offset..].chars().take_while(test) {
150            self.offset += c.len_utf8();
151        }
152    }
153
154    fn advance_until(&mut self, target: &str) {
155        while !self.eof() && !self.starts_with(target) {
156            self.advance(1);
157        }
158        self.advance(target.chars().count());
159    }
160
161    fn skip_spaces_and_comments(&mut self) {
162        loop {
163            let offset = self.offset;
164            self.advance_while(|&c| c.is_whitespace());
165            if self.starts_with("/*") {
166                self.advance(2);
167                self.advance_until("*/");
168            }
169            if offset == self.offset {
170                break;
171            }
172        }
173    }
174
175    fn skip_ident(&mut self) {
176        self.advance_while(|&c| c.is_ascii_alphanumeric() || c == '-' || c == '_');
177    }
178
179    fn skip_block(&mut self) {
180        let mut balance = 0u8;
181
182        while !self.eof() {
183            match self.next() {
184                Some('{') => {
185                    self.advance(1);
186                    balance = balance.saturating_add(1);
187                }
188                Some('}') => {
189                    self.advance(1);
190                    balance = balance.saturating_sub(1);
191                }
192                _ => break,
193            }
194            if balance == 0 {
195                break;
196            }
197            self.advance_while(|&c| c != '{' && c != '}')
198        }
199    }
200
201    fn skip_at_rule(&mut self) {
202        self.advance_while(|&c| c != ';' && c != '{');
203
204        match self.next() {
205            Some(';') => self.advance(1),
206            Some('{') => self.skip_block(),
207            _ => (),
208        }
209    }
210
211    fn attribute_value(&mut self) -> String {
212        match self.next() {
213            Some(delim @ '"' | delim @ '\'') => {
214                self.advance(1);
215                let start_offset = self.offset;
216                self.advance_while(|&c| c != delim);
217                let end_offset = self.offset;
218                self.advance(1);
219                self.input[start_offset..end_offset].to_string()
220            }
221            _ => {
222                let offset = self.offset;
223                self.skip_ident();
224                self.input[offset..self.offset].to_string()
225            }
226        }
227    }
228
229    fn parse_selectors(&mut self) -> Vec<Selector> {
230        let mut supported = true;
231        let mut selectors = Vec::new();
232        let mut s = Selector::default();
233        let mut selec = SimpleSelector::default();
234        let mut comb = Combinator::None;
235
236        while !self.eof() {
237            match self.next() {
238                Some('#') => {
239                    self.advance(1);
240                    let offset = self.offset;
241                    self.skip_ident();
242                    selec.id = Some(self.input[offset..self.offset].to_string());
243                }
244                Some('.') => {
245                    self.advance(1);
246                    let offset = self.offset;
247                    self.skip_ident();
248                    selec
249                        .classes
250                        .insert(self.input[offset..self.offset].to_string());
251                }
252                Some('[') => {
253                    self.advance(1);
254                    self.skip_spaces_and_comments();
255                    let offset = self.offset;
256                    self.skip_ident();
257                    let mut name = self.input[offset..self.offset].to_string();
258                    if self.next() == Some('|') && !self.starts_with("|=") {
259                        self.advance(1);
260                        name += ":";
261                        let offset = self.offset;
262                        self.skip_ident();
263                        name += &self.input[offset..self.offset];
264                    }
265                    self.skip_spaces_and_comments();
266                    match self.next() {
267                        Some(']') => {
268                            self.advance(1);
269                            selec.attributes.push(Attribute {
270                                name,
271                                operator: AttributeOperator::Exists,
272                            });
273                        }
274                        Some('=') => {
275                            self.advance(1);
276                            self.skip_spaces_and_comments();
277                            let value = self.attribute_value();
278                            selec.attributes.push(Attribute {
279                                name,
280                                operator: AttributeOperator::Matches(value),
281                            });
282                            self.skip_spaces_and_comments();
283                            self.advance(1);
284                        }
285                        Some('~') => {
286                            self.advance(2);
287                            self.skip_spaces_and_comments();
288                            let value = self.attribute_value();
289                            selec.attributes.push(Attribute {
290                                name,
291                                operator: AttributeOperator::Contains(value),
292                            });
293                            self.skip_spaces_and_comments();
294                            self.advance(1);
295                        }
296                        Some('|') => {
297                            self.advance(2);
298                            self.skip_spaces_and_comments();
299                            let value = self.attribute_value();
300                            selec.attributes.push(Attribute {
301                                name,
302                                operator: AttributeOperator::StartsWith(value),
303                            });
304                            self.skip_spaces_and_comments();
305                            self.advance(1);
306                        }
307                        _ => {
308                            self.advance(2);
309                            self.skip_spaces_and_comments();
310                            self.advance(1);
311                            supported = false;
312                        }
313                    }
314                }
315                Some(':') => {
316                    self.advance(1);
317                    if self.next() == Some(':') {
318                        supported = false;
319                        self.advance(1);
320                    }
321                    let offset = self.offset;
322                    self.skip_ident();
323                    match &self.input[offset..self.offset] {
324                        "first-child" => {
325                            selec.pseudo_classes.push(PseudoClass::FirstChild);
326                        }
327                        "last-child" => {
328                            selec.pseudo_classes.push(PseudoClass::LastChild);
329                        }
330                        _ => {
331                            supported = false;
332                        }
333                    }
334                    if self.next() == Some('(') {
335                        self.advance_while(|&c| c != ')');
336                        self.advance(1);
337                    }
338                }
339                Some('*') => {
340                    self.advance(1);
341                }
342                _ => {
343                    let offset = self.offset;
344                    self.skip_ident();
345                    if self.offset > offset {
346                        selec.tag_name = Some(self.input[offset..self.offset].to_string());
347                    } else {
348                        let offset = self.offset;
349                        self.skip_spaces_and_comments();
350
351                        s.simple_selectors.push(selec);
352                        s.combinators.push(comb);
353                        selec = SimpleSelector::default();
354
355                        match self.next() {
356                            Some(',') => {
357                                self.advance(1);
358                                self.skip_spaces_and_comments();
359                                if supported {
360                                    selectors.push(s);
361                                }
362                                s = Selector::default();
363                                comb = Combinator::None;
364                                supported = true;
365                            }
366                            Some('{') => {
367                                self.advance(1);
368                                break;
369                            }
370                            Some('>') => {
371                                self.advance(1);
372                                self.skip_spaces_and_comments();
373                                comb = Combinator::Child;
374                            }
375                            Some('+') => {
376                                self.advance(1);
377                                self.skip_spaces_and_comments();
378                                comb = Combinator::NextSibling;
379                            }
380                            Some('~') => {
381                                self.advance(1);
382                                self.skip_spaces_and_comments();
383                                comb = Combinator::SubsequentSibling;
384                            }
385                            _ => {
386                                if self.offset > offset {
387                                    comb = Combinator::Descendant;
388                                } else {
389                                    self.advance(1);
390                                }
391                            }
392                        }
393                    }
394                }
395            }
396        }
397
398        if supported {
399            selectors.push(s);
400        }
401
402        selectors
403    }
404
405    pub fn parse_declarations(&mut self) -> Vec<Declaration> {
406        let mut declarations = Vec::new();
407        let mut d = Declaration::default();
408
409        while !self.eof() {
410            self.skip_spaces_and_comments();
411
412            match self.next() {
413                Some(':') => {
414                    self.advance(1);
415                    self.skip_spaces_and_comments();
416                    let offset = self.offset;
417
418                    while !self.eof() {
419                        self.advance_while(|&c| c != '"' && c != ';' && c != '}' && c != '!');
420                        match self.next() {
421                            Some('"') => {
422                                self.advance(1);
423                                self.advance_while(|&c| c != '"');
424                                self.advance(1);
425                            }
426                            Some('!') => {
427                                d.important = true;
428                                break;
429                            }
430                            _ => break,
431                        }
432                    }
433
434                    d.value = self.input[offset..self.offset].trim().to_string();
435                    if d.important {
436                        self.advance_while(|&c| c != ';' && c != '}');
437                    }
438                }
439                Some(';') => {
440                    self.advance(1);
441                    declarations.push(d);
442                    d = Declaration::default();
443                }
444                Some('}') => {
445                    self.advance(1);
446                    break;
447                }
448                _ => {
449                    let offset = self.offset;
450                    self.skip_ident();
451                    if self.offset > offset {
452                        d.name = self.input[offset..self.offset].trim().to_string();
453                    } else {
454                        self.advance(1);
455                    }
456                }
457            }
458        }
459
460        if !d.name.is_empty() {
461            declarations.push(d);
462        }
463
464        declarations
465    }
466
467    fn parse_rules(&mut self, rules: &mut Vec<Rule>) {
468        let selectors = self.parse_selectors();
469        let declarations = self.parse_declarations();
470        for selector in selectors.into_iter() {
471            rules.push(Rule {
472                selector,
473                declarations: declarations.clone(),
474            });
475        }
476    }
477
478    pub fn parse(&mut self) -> StyleSheet {
479        let mut rules = Vec::new();
480
481        while !self.eof() {
482            self.skip_spaces_and_comments();
483
484            match self.next() {
485                None => break,
486                Some('@') => self.skip_at_rule(),
487                _ => self.parse_rules(&mut rules),
488            }
489        }
490
491        StyleSheet { rules }
492    }
493}
494
495#[cfg(test)]
496mod tests {
497    use super::*;
498    use tracing::debug;
499
500    #[test]
501    fn simple_css() {
502        let text = "a, .b { b: c; d: e }";
503        let css = CssParser::new(text).parse();
504        debug!("{:?}", css);
505    }
506
507    #[test]
508    fn combinators_css() {
509        let text = "a#i.j.k > b { b: c } a + .l { u: v } a { x: y }";
510        let css = CssParser::new(text).parse();
511        println!("{:?}", css);
512    }
513}