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