css_parse/syntax/
declaration_rule_list.rs

1use crate::{
2	Declaration, DeclarationValue, Kind, KindSet, Parse, Parser, Peek, Result, Span, T, ToCursors, ToSpan, diagnostics,
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>` may make use of the [AtRule][crate::AtRule] type but there is no requirement for this, but the parse steps
22/// will only parse `<R>` if an [AtKeyword][crate::token_macros::AtKeyword] can be peeked.
23///
24/// It is an [implementation of "declaration-rule-list"][1]. It includes an error tolerance in that the ending `}`
25/// token can be omitted, if at the end of the file.
26///
27/// [1]: https://drafts.csswg.org/css-syntax-3/#typedef-declaration-list
28#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(tag = "type"))]
30pub struct DeclarationRuleList<'a, D, R>
31where
32	D: DeclarationValue<'a>,
33{
34	pub open_curly: token_macros::LeftCurly,
35	pub declarations: Vec<'a, Declaration<'a, D>>,
36	pub at_rules: Vec<'a, R>,
37	pub close_curly: Option<token_macros::RightCurly>,
38}
39
40impl<'a, D, R> Peek<'a> for DeclarationRuleList<'a, D, R>
41where
42	D: DeclarationValue<'a>,
43{
44	const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::LeftCurly]);
45}
46
47impl<'a, D, R> Parse<'a> for DeclarationRuleList<'a, D, R>
48where
49	D: DeclarationValue<'a>,
50	R: Parse<'a>,
51	Declaration<'a, D>: Parse<'a>,
52{
53	fn parse(p: &mut Parser<'a>) -> Result<Self> {
54		let open_curly = p.parse::<T!['{']>()?;
55		let mut declarations = Vec::new_in(p.bump());
56		let mut at_rules = Vec::new_in(p.bump());
57		loop {
58			if p.at_end() {
59				return Ok(Self { open_curly, declarations, at_rules, close_curly: None });
60			}
61			let close_curly = p.parse_if_peek::<T!['}']>()?;
62			if close_curly.is_some() {
63				return Ok(Self { open_curly, declarations, at_rules, close_curly });
64			}
65			if p.peek::<T![AtKeyword]>() {
66				at_rules.push(p.parse::<R>()?);
67			} else if p.peek::<T![Ident]>() {
68				let rule = p.parse::<Declaration<'a, D>>()?;
69				declarations.push(rule);
70			} else {
71				Err(diagnostics::Unexpected(p.next()))?;
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}