css_parse/syntax/
declaration.rs

1use crate::{
2	BangImportant, Cursor, CursorSink, DeclarationValue, Kind, Parse, Parser, Peek, Result, Span, T, ToCursors, ToSpan,
3	token_macros,
4};
5use std::marker::PhantomData;
6
7/// This is a generic type that can be used for AST nodes representing a [Declaration][1], aka "property". This is
8/// defined as:
9///
10/// ```md
11/// <property-id>
12///  │├─ <ident> ─┤│
13///
14/// <declaration>
15///  │├─ <property-id> ─ ":" ─ <V> ──╮─────────────────────────────╭──╮───────╭┤│
16///                                  ╰─ "!" ─ <ident "important"> ─╯  ╰─ ";" ─╯
17/// ```
18///
19/// An ident is parsed first, as the property name, followed by a `:`. After this the given `<V>` will be parsed as the
20/// style value. Parsing may continue to a `!important`, or the optional trailing semi `;`, if either are present.
21///
22/// The grammar of `<V>` isn't defined here - it'll be dependant on the property name. Consequently, `<V>` must
23/// implement the [DeclarationValue] trait, which must provide the
24/// `parse_declaration_value(&mut Parser<'a>, Cursor) -> Result<Self>` method - the [Cursor] given to said method
25/// represents the Ident of the property name, so it can be reasoned about in order to dispatch to the right
26/// declaration value parsing step.
27///
28/// [1]: https://drafts.csswg.org/css-syntax-3/#consume-a-declaration
29#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(tag = "type"))]
31pub struct Declaration<'a, V: DeclarationValue<'a>> {
32	pub name: token_macros::Ident,
33	pub colon: token_macros::Colon,
34	pub value: V,
35	pub important: Option<BangImportant>,
36	pub semicolon: Option<token_macros::Semicolon>,
37	#[cfg_attr(feature = "serde", serde(skip))]
38	_phantom: PhantomData<&'a ()>,
39}
40
41impl<'a, V: DeclarationValue<'a>> Peek<'a> for Declaration<'a, V> {
42	fn peek(p: &Parser<'a>, c: Cursor) -> bool {
43		c == Kind::Ident && p.peek_n(2) == Kind::Colon
44	}
45}
46
47impl<'a, V: DeclarationValue<'a>> Parse<'a> for Declaration<'a, V> {
48	fn parse(p: &mut Parser<'a>) -> Result<Self> {
49		let name = p.parse::<T![Ident]>()?;
50		let colon = p.parse::<T![:]>()?;
51		let c: Cursor = name.into();
52		let value = <V>::parse_declaration_value(p, c)?;
53		let important = p.parse_if_peek::<BangImportant>()?;
54		let semicolon = p.parse_if_peek::<T![;]>()?;
55		Ok(Self { name, colon, value, important, semicolon, _phantom: PhantomData })
56	}
57}
58
59impl<'a, V: DeclarationValue<'a> + ToCursors> ToCursors for Declaration<'a, V> {
60	fn to_cursors(&self, s: &mut impl CursorSink) {
61		ToCursors::to_cursors(&self.name, s);
62		ToCursors::to_cursors(&self.colon, s);
63		ToCursors::to_cursors(&self.value, s);
64		ToCursors::to_cursors(&self.important, s);
65		ToCursors::to_cursors(&self.semicolon, s);
66	}
67}
68
69impl<'a, V: DeclarationValue<'a> + ToSpan> ToSpan for Declaration<'a, V> {
70	fn to_span(&self) -> Span {
71		self.name.to_span() + self.value.to_span() + self.important.to_span() + self.semicolon.to_span()
72	}
73}
74
75#[cfg(test)]
76mod tests {
77	use super::*;
78	use crate::test_helpers::*;
79
80	#[derive(Debug)]
81	struct Decl(T![Ident]);
82	impl<'a> DeclarationValue<'a> for Decl {
83		type ComputedValue = T![Eof];
84
85		fn is_initial(&self) -> bool {
86			false
87		}
88
89		fn is_inherit(&self) -> bool {
90			false
91		}
92
93		fn is_unset(&self) -> bool {
94			false
95		}
96
97		fn is_revert(&self) -> bool {
98			false
99		}
100
101		fn is_revert_layer(&self) -> bool {
102			false
103		}
104
105		fn needs_computing(&self) -> bool {
106			false
107		}
108
109		fn parse_specified_declaration_value(p: &mut Parser<'a>, _name: Cursor) -> Result<Self> {
110			p.parse::<T![Ident]>().map(Self)
111		}
112	}
113
114	impl ToCursors for Decl {
115		fn to_cursors(&self, s: &mut impl CursorSink) {
116			s.append(self.0.into())
117		}
118	}
119
120	impl ToSpan for Decl {
121		fn to_span(&self) -> Span {
122			self.0.to_span()
123		}
124	}
125
126	#[test]
127	fn size_test() {
128		assert_eq!(std::mem::size_of::<Declaration<Decl>>(), 80);
129	}
130
131	#[test]
132	fn test_writes() {
133		assert_parse!(Declaration<Decl>, "color:black;");
134	}
135}