css_parse/traits/style_sheet.rs
1use crate::{Cursor, Kind, NodeMetadata, NodeWithMetadata, Parse, Parser, Result, T};
2use bumpalo::collections::Vec;
3
4/// This trait provides an implementation for parsing a [StyleSheet][1].
5///
6/// [1]: https://drafts.csswg.org/css-syntax-3/#parse-stylesheet
7///
8/// It does not implement [Parse], but provides `parse_stylesheet(&mut Parser<'a>) -> Result<...>`, which can make
9/// for a trivial [Parse] implementation. The type [StyleSheet::Rule] must be defined, and represents any Rule allowed
10/// in a style sheet, which is the only top level item of the stylesheet.
11///
12/// StyleSheets are special in that they must discard CdcOrCdo tokens.
13///
14/// The steps `parse_stylesheet` takes can be defined as:
15///
16/// ```md
17/// <style-sheet>
18/// │├─╮─╭─ <ws*> ─╮─╭╮─╭─ <cdcorcdo-token> ─╮─╭─ <rule> ──┤│
19/// │ ╰─────────╯ ││ ╰────────────────────╯ │
20/// ╰─────────────╯╰────────────────────────╯
21/// ```
22///
23pub trait StyleSheet<'a, M: NodeMetadata>: Sized + Parse<'a> {
24 type Rule: Parse<'a> + NodeWithMetadata<M>;
25
26 fn parse_stylesheet<I>(p: &mut Parser<'a, I>) -> Result<(Vec<'a, Self::Rule>, M)>
27 where
28 I: Iterator<Item = Cursor> + Clone,
29 {
30 let mut rules: Vec<'a, Self::Rule> = Vec::new_in(p.bump());
31 let mut meta: M = Default::default();
32 loop {
33 // While by default the parser will skip whitespace, the Rule type may be a whitespace sensitive
34 // node, for example `ComponentValues`. As such whitespace needs to be consumed here, before Declarations and
35 // Rules are parsed.
36 if p.parse_if_peek::<T![' ']>()?.is_some() || p.parse_if_peek::<T![CdcOrCdo]>()?.is_some() {
37 continue;
38 }
39
40 // need to peek as last tokens may be whitespace.
41 if p.at_end() || p.peek_n(1) == Kind::Eof {
42 return Ok((rules, meta));
43 }
44 let rule = p.parse::<Self::Rule>()?;
45 meta.merge(&rule.metadata());
46 rules.push(rule);
47 }
48 }
49}