css_parse/syntax/
block.rs

1use crate::{
2	CursorSink, DeclarationValue, Kind, KindSet, NodeMetadata, NodeWithMetadata, Parse, Parser, Peek, Result,
3	SemanticEq, Span, State, T, ToCursors, ToSpan, token_macros,
4};
5use bumpalo::collections::Vec;
6
7use super::Declaration;
8
9/// This trait provides an implementation for ["consuming a blocks contents"][1].
10///
11/// ```md
12/// <block>
13///
14///  │├─ "{" ─╭──╮─╭─ <ws-*> ─╮─╭─╮─╭─ ";" ─╮─╭─╮─ <R> ─╭─╮─ "}" ─┤│
15///           │  │ ╰──────────╯ │ │ ╰───────╯ │ ├─ <D> ─┤ │
16///           │  ╰──────────────╯ ╰───────────╯ ╰───────╯ │
17///           ╰───────────────────────────────────────────╯
18/// ```
19///
20/// [1]: https://drafts.csswg.org/css-syntax-3/#consume-block-contents
21#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize))]
23#[cfg_attr(feature = "serde", serde(bound(serialize = "D: serde::Serialize, R: serde::Serialize")))]
24pub struct Block<'a, D, R, M>
25where
26	D: DeclarationValue<'a, M>,
27	M: NodeMetadata,
28{
29	pub open_curly: token_macros::LeftCurly,
30	pub declarations: Vec<'a, Declaration<'a, D, M>>,
31	pub rules: Vec<'a, R>,
32	pub close_curly: Option<token_macros::RightCurly>,
33	#[cfg_attr(feature = "serde", serde(skip))]
34	pub meta: M,
35}
36
37impl<'a, D, R, M> NodeWithMetadata<M> for Block<'a, D, R, M>
38where
39	D: DeclarationValue<'a, M>,
40	M: NodeMetadata,
41{
42	fn metadata(&self) -> M {
43		self.meta
44	}
45}
46
47impl<'a, D, R, M> Peek<'a> for Block<'a, D, R, M>
48where
49	D: DeclarationValue<'a, M>,
50	M: NodeMetadata,
51{
52	const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::LeftCurly]);
53}
54
55impl<'a, D, R, M> Parse<'a> for Block<'a, D, R, M>
56where
57	D: DeclarationValue<'a, M>,
58	R: Parse<'a> + NodeWithMetadata<M>,
59	M: NodeMetadata,
60{
61	fn parse<Iter>(p: &mut Parser<'a, Iter>) -> Result<Self>
62	where
63		Iter: Iterator<Item = crate::Cursor> + Clone,
64	{
65		let open_curly = p.parse::<T!['{']>()?;
66		let mut declarations = Vec::new_in(p.bump());
67		let mut rules = Vec::new_in(p.bump());
68		let mut meta = M::default();
69		loop {
70			// While by default the parser will skip whitespace, the Declaration or Rule type may be a whitespace sensitive
71			// node, for example `ComponentValues`. As such whitespace needs to be consumed here, before Declarations and
72			// Rules are parsed.
73			if p.parse_if_peek::<T![' ']>()?.is_some() || p.parse_if_peek::<T![;]>()?.is_some() {
74				continue;
75			}
76			if p.at_end() {
77				break;
78			}
79			let c = p.peek_n(1);
80			if <T!['}']>::peek(p, c) {
81				break;
82			}
83			let old_state = p.set_state(State::Nested);
84			if <T![AtKeyword]>::peek(p, c) {
85				let rule = p.parse::<R>();
86				p.set_state(old_state);
87				let rule = rule?;
88				meta.merge(&rule.metadata());
89				rules.push(rule);
90			} else if let Ok(Some(decl)) = p.try_parse_if_peek::<Declaration<'a, D, M>>() {
91				p.set_state(old_state);
92				meta.merge(&decl.metadata());
93				declarations.push(decl);
94			} else {
95				let rule = p.parse::<R>();
96				p.set_state(old_state);
97				let rule = rule?;
98				meta.merge(&rule.metadata());
99				rules.push(rule);
100			}
101		}
102		let close_curly = p.parse_if_peek::<T!['}']>()?;
103		Ok(Self { open_curly, declarations, rules, close_curly, meta })
104	}
105}
106
107impl<'a, D, R, M> ToCursors for Block<'a, D, R, M>
108where
109	D: DeclarationValue<'a, M> + ToCursors,
110	R: ToCursors,
111	M: NodeMetadata,
112{
113	fn to_cursors(&self, s: &mut impl CursorSink) {
114		ToCursors::to_cursors(&self.open_curly, s);
115		ToCursors::to_cursors(&self.declarations, s);
116		ToCursors::to_cursors(&self.rules, s);
117		ToCursors::to_cursors(&self.close_curly, s);
118	}
119}
120
121impl<'a, D, R, M> ToSpan for Block<'a, D, R, M>
122where
123	D: DeclarationValue<'a, M> + ToSpan,
124	R: ToSpan,
125	M: NodeMetadata,
126{
127	fn to_span(&self) -> Span {
128		self.open_curly.to_span()
129			+ if self.close_curly.is_some() {
130				self.close_curly.to_span()
131			} else {
132				self.declarations.to_span() + self.rules.to_span() + self.close_curly.to_span()
133			}
134	}
135}
136
137impl<'a, D, R, M> SemanticEq for Block<'a, D, R, M>
138where
139	D: DeclarationValue<'a, M>,
140	R: SemanticEq,
141	M: NodeMetadata,
142{
143	fn semantic_eq(&self, other: &Self) -> bool {
144		self.open_curly.semantic_eq(&other.open_curly)
145			&& self.close_curly.semantic_eq(&other.close_curly)
146			&& self.declarations.semantic_eq(&other.declarations)
147			&& self.rules.semantic_eq(&other.rules)
148	}
149}
150
151#[cfg(test)]
152mod tests {
153	use super::*;
154	use crate::EmptyAtomSet;
155	use crate::{Cursor, test_helpers::*};
156
157	#[derive(Debug)]
158	struct Decl(T![Ident]);
159
160	impl<M: NodeMetadata> NodeWithMetadata<M> for Decl {
161		fn metadata(&self) -> M {
162			M::default()
163		}
164	}
165
166	impl<'a, M: NodeMetadata> DeclarationValue<'a, M> for Decl {
167		type ComputedValue = T![Eof];
168
169		fn is_initial(&self) -> bool {
170			false
171		}
172
173		fn is_inherit(&self) -> bool {
174			false
175		}
176
177		fn is_unset(&self) -> bool {
178			false
179		}
180
181		fn is_revert(&self) -> bool {
182			false
183		}
184
185		fn is_revert_layer(&self) -> bool {
186			false
187		}
188
189		fn needs_computing(&self) -> bool {
190			false
191		}
192
193		fn parse_specified_declaration_value<Iter>(p: &mut Parser<'a, Iter>, _: Cursor) -> Result<Self>
194		where
195			Iter: Iterator<Item = crate::Cursor> + Clone,
196		{
197			p.parse::<T![Ident]>().map(Self)
198		}
199	}
200
201	impl ToCursors for Decl {
202		fn to_cursors(&self, s: &mut impl CursorSink) {
203			ToCursors::to_cursors(&self.0, s);
204		}
205	}
206
207	impl ToSpan for Decl {
208		fn to_span(&self) -> Span {
209			self.0.to_span()
210		}
211	}
212
213	impl SemanticEq for Decl {
214		fn semantic_eq(&self, other: &Self) -> bool {
215			self.0.semantic_eq(&other.0)
216		}
217	}
218
219	impl NodeWithMetadata<()> for T![Ident] {
220		fn metadata(&self) -> () {}
221	}
222
223	#[test]
224	fn size_test() {
225		assert_eq!(std::mem::size_of::<Block<Decl, T![Ident], ()>>(), 96);
226	}
227
228	#[test]
229	fn test_writes() {
230		assert_parse!(EmptyAtomSet::ATOMS, Block<Decl, T![Ident], ()>, "{color:black}");
231	}
232}