css_parse/syntax/
declaration_rule_list.rs

1use crate::{
2	Declaration, DeclarationValue, Diagnostic, Kind, KindSet, NodeMetadata, NodeWithMetadata, Parse, Parser, Peek,
3	Result, Span, T, ToCursors, ToSpan, 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, M>
30where
31	D: DeclarationValue<'a, M>,
32	R: NodeWithMetadata<M>,
33	M: NodeMetadata,
34{
35	pub open_curly: token_macros::LeftCurly,
36	pub declarations: Vec<'a, Declaration<'a, D, M>>,
37	pub at_rules: Vec<'a, R>,
38	pub close_curly: Option<token_macros::RightCurly>,
39	meta: M,
40}
41
42impl<'a, D, R, M> NodeWithMetadata<M> for DeclarationRuleList<'a, D, R, M>
43where
44	D: DeclarationValue<'a, M>,
45	R: NodeWithMetadata<M>,
46	M: NodeMetadata,
47{
48	fn metadata(&self) -> M {
49		self.meta
50	}
51}
52
53impl<'a, D, R, M> Peek<'a> for DeclarationRuleList<'a, D, R, M>
54where
55	D: DeclarationValue<'a, M>,
56	R: NodeWithMetadata<M>,
57	M: NodeMetadata,
58{
59	const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::LeftCurly]);
60}
61
62impl<'a, D, R, M> Parse<'a> for DeclarationRuleList<'a, D, R, M>
63where
64	D: DeclarationValue<'a, M>,
65	R: NodeWithMetadata<M> + Parse<'a>,
66	M: NodeMetadata,
67	Declaration<'a, D, M>: Parse<'a>,
68{
69	fn parse<Iter>(p: &mut Parser<'a, Iter>) -> Result<Self>
70	where
71		Iter: Iterator<Item = crate::Cursor> + Clone,
72	{
73		let open_curly = p.parse::<T!['{']>()?;
74		let mut declarations = Vec::new_in(p.bump());
75		let mut at_rules = Vec::new_in(p.bump());
76		let mut meta = Default::default();
77		loop {
78			if p.at_end() {
79				return Ok(Self { open_curly, declarations, at_rules, meta, close_curly: None });
80			}
81			let close_curly = p.parse_if_peek::<T!['}']>()?;
82			if close_curly.is_some() {
83				return Ok(Self { open_curly, declarations, at_rules, meta, close_curly });
84			}
85			let c = p.peek_n(1);
86			if <T![AtKeyword]>::peek(p, c) {
87				at_rules.push(p.parse::<R>()?);
88			} else if <T![Ident]>::peek(p, c) {
89				let rule = p.parse::<Declaration<'a, D, M>>()?;
90				meta.merge(&rule.metadata());
91				declarations.push(rule);
92			} else {
93				Err(Diagnostic::new(p.next(), Diagnostic::unexpected))?;
94			}
95		}
96	}
97}
98
99impl<'a, D, R, M> ToCursors for DeclarationRuleList<'a, D, R, M>
100where
101	D: DeclarationValue<'a, M> + ToCursors,
102	R: NodeWithMetadata<M> + ToCursors,
103	M: NodeMetadata,
104{
105	fn to_cursors(&self, s: &mut impl crate::CursorSink) {
106		ToCursors::to_cursors(&self.open_curly, s);
107		ToCursors::to_cursors(&self.declarations, s);
108		ToCursors::to_cursors(&self.at_rules, s);
109		ToCursors::to_cursors(&self.close_curly, s);
110	}
111}
112
113impl<'a, D, R, M> ToSpan for DeclarationRuleList<'a, D, R, M>
114where
115	D: DeclarationValue<'a, M> + ToSpan,
116	R: NodeWithMetadata<M> + ToSpan,
117	M: NodeMetadata,
118{
119	fn to_span(&self) -> Span {
120		self.open_curly.to_span()
121			+ if let Some(close) = self.close_curly {
122				close.to_span()
123			} else {
124				self.declarations.to_span() + self.at_rules.to_span()
125			}
126	}
127}