css_parse/syntax/
declaration_list.rs

1use crate::{
2	CursorSink, Declaration, DeclarationValue, Kind, KindSet, NodeMetadata, NodeWithMetadata, Parse, Parser, Peek,
3	Result, SemanticEq, Span, T, ToCursors, ToSpan, token_macros,
4};
5use bumpalo::collections::Vec;
6
7/// A generic struct that can be used for AST nodes representing a rule's block, that is only capable of having child
8/// declarations.
9///
10/// It is an [implementation of "declaration-list"][1]. It includes an error tolerance in that the ending `}` token can
11/// be omitted, if at the end of the file.
12///
13/// The `<V>` must implement the [DeclarationValue] trait, as it is passed to [Declaration].
14///
15/// ```md
16/// <declaration-list>
17///  │├─ "{" ─╮─╭─ <declaration> ──╮─╭─╮─ "}" ─╭─┤│
18///           │ │                  │ │ ╰───────╯
19///           │ ╰──────────────────╯ │
20///           ╰──────────────────────╯
21/// ```
22///
23/// [1]: https://drafts.csswg.org/css-syntax-3/#typedef-declaration-list
24#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize))]
26#[cfg_attr(feature = "serde", serde(bound(serialize = "V: serde::Serialize")))]
27pub struct DeclarationList<'a, V, M>
28where
29	V: DeclarationValue<'a, M>,
30	M: NodeMetadata,
31{
32	pub open_curly: token_macros::LeftCurly,
33	pub declarations: Vec<'a, Declaration<'a, V, M>>,
34	pub close_curly: Option<token_macros::RightCurly>,
35	#[cfg_attr(feature = "serde", serde(skip))]
36	meta: M,
37}
38
39impl<'a, V, M> NodeWithMetadata<M> for DeclarationList<'a, V, M>
40where
41	V: DeclarationValue<'a, M>,
42	M: NodeMetadata,
43{
44	fn metadata(&self) -> M {
45		self.meta
46	}
47}
48
49impl<'a, V, M> Peek<'a> for DeclarationList<'a, V, M>
50where
51	V: DeclarationValue<'a, M>,
52	M: NodeMetadata,
53{
54	const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::LeftCurly]);
55}
56
57impl<'a, V, M> Parse<'a> for DeclarationList<'a, V, M>
58where
59	V: DeclarationValue<'a, M>,
60	M: NodeMetadata,
61{
62	fn parse<Iter>(p: &mut Parser<'a, Iter>) -> Result<Self>
63	where
64		Iter: Iterator<Item = crate::Cursor> + Clone,
65	{
66		let open_curly = p.parse::<T!['{']>()?;
67		let mut declarations = Vec::new_in(p.bump());
68		let mut meta = Default::default();
69		loop {
70			if p.at_end() {
71				return Ok(Self { open_curly, declarations, close_curly: None, meta });
72			}
73			let close_curly = p.parse_if_peek::<T!['}']>()?;
74			if close_curly.is_some() {
75				return Ok(Self { open_curly, declarations, close_curly, meta });
76			}
77			let declaration = p.parse::<Declaration<'a, V, M>>()?;
78			meta.merge(&declaration.metadata());
79			declarations.push(declaration);
80		}
81	}
82}
83
84impl<'a, V, M> ToCursors for DeclarationList<'a, V, M>
85where
86	V: DeclarationValue<'a, M> + ToCursors,
87	M: NodeMetadata,
88{
89	fn to_cursors(&self, s: &mut impl CursorSink) {
90		ToCursors::to_cursors(&self.open_curly, s);
91		ToCursors::to_cursors(&self.declarations, s);
92		ToCursors::to_cursors(&self.close_curly, s);
93	}
94}
95
96impl<'a, V, M> ToSpan for DeclarationList<'a, V, M>
97where
98	V: DeclarationValue<'a, M> + ToSpan,
99	M: NodeMetadata,
100{
101	fn to_span(&self) -> Span {
102		self.open_curly.to_span()
103			+ if let Some(close) = self.close_curly { close.to_span() } else { self.declarations.to_span() }
104	}
105}
106
107impl<'a, V, M> SemanticEq for DeclarationList<'a, V, M>
108where
109	V: DeclarationValue<'a, M>,
110	M: NodeMetadata,
111{
112	fn semantic_eq(&self, other: &Self) -> bool {
113		self.open_curly.semantic_eq(&other.open_curly)
114			&& self.declarations.semantic_eq(&other.declarations)
115			&& self.close_curly.semantic_eq(&other.close_curly)
116	}
117}