Skip to main content

css_parse/syntax/
rule_list.rs

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