css_parse/syntax/
function.rs

1use crate::{Cursor, CursorSink, Parse, Parser, Peek, Result as ParserResult, Span, ToCursors, ToSpan, token_macros};
2use std::marker::PhantomData;
3
4/// This struct provides the generic `function()` grammar that parses a [function block][1] where the interior function
5/// parameters are `<T>` and the function name is `<FT>`. The grammar is:
6///
7/// ```md
8/// <function>
9///  │├─ <FT> ─ <T> ─ ")" ─┤│
10/// ```
11///
12/// `<AT>` must implement [Peek],  [Parse], and `Into<token_macros::Function>`. This helps enforce that this is a
13/// function, that the first token has to be a specific function token.
14///
15/// `<T>` - the interior parameters - must implement [Parse], [ToCursors], and [ToSpan].
16///
17/// To specify extra restrictions on the value of the function, use [function_set][crate::function_set].
18///
19/// # Example
20///
21/// ```
22/// use css_parse::*;
23///
24/// /// A grammar like `test(foo)`
25/// #[derive(Debug)]
26/// pub struct TestFunction(Function<T![Function], T![Ident]>);
27///
28/// impl<'a> Parse<'a> for TestFunction {
29///     fn parse(p: &mut Parser<'a>) -> Result<Self> {
30///         p.parse::<Function<T![Function], T![Ident]>>().map(Self)
31///     }
32/// }
33///
34/// impl ToCursors for TestFunction {
35///     fn to_cursors(&self, s: &mut impl CursorSink) {
36///         self.0.to_cursors(s);
37///     }
38/// }
39///
40/// assert_parse!(TestFunction, "test(foo)");
41/// ```
42///
43///
44/// [1]: https://drafts.csswg.org/css-syntax-3/#function-block-diagram
45/// [2]: https://drafts.csswg.org/css-syntax-3/#consume-function
46#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
48pub struct Function<FT, T>
49where
50	FT: Into<token_macros::Function>,
51{
52	pub name: token_macros::Function,
53	pub parameters: T,
54	pub close: Option<token_macros::RightParen>,
55	#[cfg_attr(feature = "serde", serde(skip))]
56	_phantom: PhantomData<FT>,
57}
58
59impl<'a, FT, T> Peek<'a> for Function<FT, T>
60where
61	FT: Peek<'a> + Into<token_macros::Function>,
62{
63	fn peek(p: &Parser<'a>, c: Cursor) -> bool {
64		<FT>::peek(p, c)
65	}
66}
67
68impl<'a, FT, T> Parse<'a> for Function<FT, T>
69where
70	FT: Parse<'a> + Into<token_macros::Function>,
71	T: Parse<'a>,
72{
73	fn parse(p: &mut Parser<'a>) -> ParserResult<Self> {
74		let name = p.parse::<FT>()?.into();
75		let parameters = p.parse::<T>()?;
76		let close = p.parse_if_peek::<token_macros::RightParen>()?;
77		Ok(Self { name, parameters, close, _phantom: PhantomData })
78	}
79}
80
81impl<FT, T> ToCursors for Function<FT, T>
82where
83	FT: Into<token_macros::Function>,
84	T: ToCursors,
85{
86	fn to_cursors(&self, s: &mut impl CursorSink) {
87		s.append(self.name.into());
88		ToCursors::to_cursors(&self.parameters, s);
89		ToCursors::to_cursors(&self.close, s);
90	}
91}
92
93impl<FT, T> ToSpan for Function<FT, T>
94where
95	FT: Into<token_macros::Function>,
96	T: ToSpan,
97{
98	fn to_span(&self) -> Span {
99		self.name.to_span() + self.parameters.to_span() + self.close.to_span()
100	}
101}
102
103#[cfg(test)]
104mod tests {
105	use super::*;
106	use crate::{T, test_helpers::*};
107
108	type FunctionBlock<'a> = Function<T![Function], T![Ident]>;
109
110	#[test]
111	fn size_test() {
112		assert_eq!(std::mem::size_of::<FunctionBlock>(), 40);
113	}
114
115	#[test]
116	fn test_writes() {
117		assert_parse!(FunctionBlock, "foo(bar)");
118	}
119}