css_parse/syntax/
declaration_rule_list.rs

1use crate::{
2	Declaration, DeclarationValue, Diagnostic, Kind, KindSet, Parse, Parser, Peek, Result, Span, T, ToCursors, ToSpan,
3	token_macros,
4};
5use bumpalo::collections::Vec;
6
7/// A generic struct for AST nodes representing a rule's block that is only capable of having child declarations or
8/// at-rules. Qualified Rules are not allowed. This is defined as:
9///
10/// ```md
11/// <declaration-rule-list>
12///  │├─ "{" ─╮─╭─╮─ <declaration <D>> ─╮─╭─╮─ "}" ─╭─┤│
13///           │ │ │                     │ │ ╰───────╯
14///           │ │ ╰─ <R> ───────────────┤ │
15///           │ ╰───────────────────────╯ │
16///           ╰───────────────────────────╯
17/// ```
18///
19/// `<D>` must implement the [Declaration][crate::Declaration] trait.
20///
21/// `<R>` should be an At-Rule. `<R>` is only parsed if an [AtKeyword][crate::token_macros::AtKeyword] can be peeked.
22///
23/// It is an [implementation of "declaration-rule-list"][1]. It includes an error tolerance in that the ending `}`
24/// token can be omitted, if at the end of the file.
25///
26/// [1]: https://drafts.csswg.org/css-syntax-3/#typedef-declaration-list
27#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
29pub struct DeclarationRuleList<'a, D, R>
30where
31	D: DeclarationValue<'a>,
32{
33	pub open_curly: token_macros::LeftCurly,
34	pub declarations: Vec<'a, Declaration<'a, D>>,
35	pub at_rules: Vec<'a, R>,
36	pub close_curly: Option<token_macros::RightCurly>,
37}
38
39impl<'a, D, R> Peek<'a> for DeclarationRuleList<'a, D, R>
40where
41	D: DeclarationValue<'a>,
42{
43	const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::LeftCurly]);
44}
45
46impl<'a, D, R> Parse<'a> for DeclarationRuleList<'a, D, R>
47where
48	D: DeclarationValue<'a>,
49	R: Parse<'a>,
50	Declaration<'a, D>: Parse<'a>,
51{
52	fn parse(p: &mut Parser<'a>) -> Result<Self> {
53		let open_curly = p.parse::<T!['{']>()?;
54		let mut declarations = Vec::new_in(p.bump());
55		let mut at_rules = Vec::new_in(p.bump());
56		loop {
57			if p.at_end() {
58				return Ok(Self { open_curly, declarations, at_rules, close_curly: None });
59			}
60			let close_curly = p.parse_if_peek::<T!['}']>()?;
61			if close_curly.is_some() {
62				return Ok(Self { open_curly, declarations, at_rules, close_curly });
63			}
64			let c = p.peek_n(1);
65			if <T![AtKeyword]>::peek(p, c) {
66				at_rules.push(p.parse::<R>()?);
67			} else if <T![Ident]>::peek(p, c) {
68				let rule = p.parse::<Declaration<'a, D>>()?;
69				declarations.push(rule);
70			} else {
71				Err(Diagnostic::new(p.next(), Diagnostic::unexpected))?;
72			}
73		}
74	}
75}
76
77impl<'a, D, R> ToCursors for DeclarationRuleList<'a, D, R>
78where
79	D: DeclarationValue<'a> + ToCursors,
80	R: ToCursors,
81{
82	fn to_cursors(&self, s: &mut impl crate::CursorSink) {
83		ToCursors::to_cursors(&self.open_curly, s);
84		ToCursors::to_cursors(&self.declarations, s);
85		ToCursors::to_cursors(&self.at_rules, s);
86		ToCursors::to_cursors(&self.close_curly, s);
87	}
88}
89
90impl<'a, D, R> ToSpan for DeclarationRuleList<'a, D, R>
91where
92	D: DeclarationValue<'a> + ToSpan,
93	R: ToSpan,
94{
95	fn to_span(&self) -> Span {
96		self.open_curly.to_span()
97			+ if let Some(close) = self.close_curly {
98				close.to_span()
99			} else {
100				self.declarations.to_span() + self.at_rules.to_span()
101			}
102	}
103}