css_parse/syntax/
block.rs

1use crate::{
2	CursorSink, DeclarationValue, Kind, KindSet, Parse, Parser, Peek, Result, Span, State, T, ToCursors, ToSpan,
3	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), serde())]
23pub struct Block<'a, D, R>
24where
25	D: DeclarationValue<'a>,
26{
27	pub open_curly: token_macros::LeftCurly,
28	pub declarations: Vec<'a, Declaration<'a, D>>,
29	pub rules: Vec<'a, R>,
30	pub close_curly: Option<token_macros::RightCurly>,
31}
32
33impl<'a, D, R> Peek<'a> for Block<'a, D, R>
34where
35	D: DeclarationValue<'a>,
36{
37	const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::LeftCurly]);
38}
39
40impl<'a, D, R> Parse<'a> for Block<'a, D, R>
41where
42	D: DeclarationValue<'a>,
43	R: Parse<'a>,
44{
45	fn parse<Iter>(p: &mut Parser<'a, Iter>) -> Result<Self>
46	where
47		Iter: Iterator<Item = crate::Cursor> + Clone,
48	{
49		let open_curly = p.parse::<T!['{']>()?;
50		let mut declarations = Vec::new_in(p.bump());
51		let mut rules = Vec::new_in(p.bump());
52		loop {
53			// While by default the parser will skip whitespace, the Declaration or Rule type may be a whitespace sensitive
54			// node, for example `ComponentValues`. As such whitespace needs to be consumed here, before Declarations and
55			// Rules are parsed.
56			if p.parse_if_peek::<T![' ']>()?.is_some() || p.parse_if_peek::<T![;]>()?.is_some() {
57				continue;
58			}
59			if p.at_end() {
60				break;
61			}
62			let c = p.peek_n(1);
63			if <T!['}']>::peek(p, c) {
64				break;
65			}
66			let old_state = p.set_state(State::Nested);
67			if <T![AtKeyword]>::peek(p, c) {
68				let rule = p.parse::<R>();
69				p.set_state(old_state);
70				rules.push(rule?);
71			} else if let Ok(Some(decl)) = p.try_parse_if_peek::<Declaration<'a, D>>() {
72				p.set_state(old_state);
73				declarations.push(decl);
74			} else {
75				let rule = p.parse::<R>();
76				p.set_state(old_state);
77				rules.push(rule?);
78			}
79		}
80		let close_curly = p.parse_if_peek::<T!['}']>()?;
81		Ok(Self { open_curly, declarations, rules, close_curly })
82	}
83}
84
85impl<'a, D, R> ToCursors for Block<'a, D, R>
86where
87	D: DeclarationValue<'a> + ToCursors,
88	R: ToCursors,
89{
90	fn to_cursors(&self, s: &mut impl CursorSink) {
91		ToCursors::to_cursors(&self.open_curly, s);
92		ToCursors::to_cursors(&self.declarations, s);
93		ToCursors::to_cursors(&self.rules, s);
94		ToCursors::to_cursors(&self.close_curly, s);
95	}
96}
97
98impl<'a, D, R> ToSpan for Block<'a, D, R>
99where
100	D: DeclarationValue<'a> + ToSpan,
101	R: ToSpan,
102{
103	fn to_span(&self) -> Span {
104		self.open_curly.to_span()
105			+ if self.close_curly.is_some() {
106				self.close_curly.to_span()
107			} else {
108				self.declarations.to_span() + self.rules.to_span() + self.close_curly.to_span()
109			}
110	}
111}
112
113#[cfg(test)]
114mod tests {
115	use super::*;
116	use crate::EmptyAtomSet;
117	use crate::{Cursor, test_helpers::*};
118
119	#[derive(Debug)]
120	struct Decl(T![Ident]);
121	impl<'a> DeclarationValue<'a> for Decl {
122		type ComputedValue = T![Eof];
123
124		fn is_initial(&self) -> bool {
125			false
126		}
127
128		fn is_inherit(&self) -> bool {
129			false
130		}
131
132		fn is_unset(&self) -> bool {
133			false
134		}
135
136		fn is_revert(&self) -> bool {
137			false
138		}
139
140		fn is_revert_layer(&self) -> bool {
141			false
142		}
143
144		fn needs_computing(&self) -> bool {
145			false
146		}
147
148		fn parse_specified_declaration_value<Iter>(p: &mut Parser<'a, Iter>, _: Cursor) -> Result<Self>
149		where
150			Iter: Iterator<Item = crate::Cursor> + Clone,
151		{
152			p.parse::<T![Ident]>().map(Self)
153		}
154	}
155
156	impl ToCursors for Decl {
157		fn to_cursors(&self, s: &mut impl CursorSink) {
158			ToCursors::to_cursors(&self.0, s);
159		}
160	}
161
162	impl ToSpan for Decl {
163		fn to_span(&self) -> Span {
164			self.0.to_span()
165		}
166	}
167
168	#[test]
169	fn size_test() {
170		assert_eq!(std::mem::size_of::<Block<Decl, T![Ident]>>(), 96);
171	}
172
173	#[test]
174	fn test_writes() {
175		assert_parse!(EmptyAtomSet::ATOMS, Block<Decl, T![Ident]>, "{color:black}");
176	}
177}