css_parse/syntax/
rule_list.rs

1use crate::{Cursor, CursorSink, Parse, Parser, Peek, Result, T, ToCursors, ToSpan, token_macros};
2use bumpalo::collections::Vec;
3
4/// A struct representing an AST node block that only accepts child "Rules". This is defined as:
5///
6/// ```md
7/// <rule-list>
8///  │├─ "{" ─╭─ <R> ─╮─╮─ "}" ─╭──┤│
9///           ╰───────╯ ╰───────╯
10/// ```
11///
12/// This is an implementation of [`<at-rule-list>`][1] or [`<qualified-rule-list>`][2].
13///
14/// It simply parses the open `{` and iterates collecing `<R>`s until the closing `}`.
15///
16/// Every item in the list must implement the [Parse], [ToCursors] and [ToSpan] traits.
17///
18/// [1]: https://drafts.csswg.org/css-syntax-3/#typedef-at-rule-list
19/// [2]: https://drafts.csswg.org/css-syntax-3/#typedef-qualified-rule-list
20#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(tag = "type"))]
22pub struct RuleList<'a, R> {
23	pub open_curly: token_macros::LeftCurly,
24	pub rules: Vec<'a, R>,
25	pub close_curly: Option<token_macros::RightCurly>,
26}
27
28impl<'a, R> Peek<'a> for RuleList<'a, R> {
29	fn peek(p: &Parser<'a>, c: Cursor) -> bool {
30		<token_macros::LeftCurly>::peek(p, c)
31	}
32}
33
34impl<'a, R: Parse<'a>> Parse<'a> for RuleList<'a, R> {
35	fn parse(p: &mut Parser<'a>) -> Result<Self> {
36		let open_curly = p.parse::<T!['{']>()?;
37		let mut rules = Vec::new_in(p.bump());
38		loop {
39			p.parse_if_peek::<T![;]>().ok();
40			if p.at_end() {
41				return Ok(Self { open_curly, rules, close_curly: None });
42			}
43			let close_curly = p.parse_if_peek::<T!['}']>()?;
44			if close_curly.is_some() {
45				return Ok(Self { open_curly, rules, close_curly });
46			}
47			rules.push(p.parse::<R>()?);
48		}
49	}
50}
51
52impl<'a, R: ToCursors> ToCursors for RuleList<'a, R> {
53	fn to_cursors(&self, s: &mut impl CursorSink) {
54		ToCursors::to_cursors(&self.open_curly, s);
55		ToCursors::to_cursors(&self.rules, s);
56		ToCursors::to_cursors(&self.close_curly, s);
57	}
58}
59
60impl<'a, R: ToSpan> ToSpan for RuleList<'a, R> {
61	fn to_span(&self) -> css_lexer::Span {
62		self.open_curly.to_span()
63			+ if let Some(close) = self.close_curly { close.to_span() } else { self.rules.to_span() }
64	}
65}