css_ast/functions/
target_functions.rs

1use css_parse::{Function, T, function_set, keyword_set};
2use csskit_derives::{IntoCursor, Parse, Peek, ToCursors, ToSpan, Visitable};
3
4use crate::types::CounterStyle;
5
6keyword_set!(
7	pub enum TextFunctionContent {
8		Content: "content",
9		Before: "before",
10		After: "after",
11		FirstLetter: "first-letter"
12	}
13);
14
15#[derive(Parse, Peek, ToCursors, IntoCursor, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
17pub enum TargetCounterKind {
18	String(T![String]),
19	Url(T![Url]),
20}
21
22/// <https://drafts.csswg.org/css-content-3/#typedef-target>
23///
24/// ```text,ignore
25/// <target> = <target-counter()> | <target-counters()> | <target-text()>
26/// ```
27#[derive(Parse, Peek, ToSpan, ToCursors, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
29#[visit]
30pub enum Target<'a> {
31	// https://drafts.csswg.org/css-content-3/#target-counter
32	// target-counter() = target-counter( [ <string> | <url> ] , <custom-ident> , <counter-style>? )
33	TargetCounter(TargetCounterFunction<'a>),
34	// https://drafts.csswg.org/css-content-3/#target-counters
35	// target-counters() = target-counters( [ <string> | <url> ] , <custom-ident> , <string> , <counter-style>? )
36	TargetCounters(TargetCountersFunction<'a>),
37	// https://drafts.csswg.org/css-content-3/#target-text
38	// target-text() = target-text( [ <string> | <url> ] , [ content | before | after | first-letter ]? )
39	TargetText(TargetTextFunction),
40}
41
42function_set!(pub struct TargetCounterFunctionName "target-counter");
43
44#[derive(Parse, Peek, ToSpan, ToCursors, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
46#[visit(self)]
47pub struct TargetCounterFunction<'a>(Function<TargetCounterFunctionName, TargetCounterParams<'a>>);
48
49#[derive(Parse, Peek, ToSpan, ToCursors, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
50#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
51pub struct TargetCounterParams<'a>(
52	TargetCounterKind,
53	Option<T![,]>,
54	T![Ident],
55	Option<T![,]>,
56	Option<CounterStyle<'a>>,
57);
58
59function_set!(pub struct TargetCountersFunctionName "target-counters");
60
61#[derive(Parse, Peek, ToSpan, ToCursors, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
62#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
63#[visit(self)]
64pub struct TargetCountersFunction<'a>(Function<TargetCountersFunctionName, TargetCountersParams<'a>>);
65
66#[derive(Parse, Peek, ToSpan, ToCursors, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
68pub struct TargetCountersParams<'a>(
69	TargetCounterKind,
70	Option<T![,]>,
71	T![Ident],
72	Option<T![,]>,
73	T![String],
74	Option<T![,]>,
75	Option<CounterStyle<'a>>,
76);
77
78function_set!(pub struct TargetTextFunctionName "target-text");
79
80#[derive(Parse, Peek, ToSpan, ToCursors, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
81#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
82#[visit(self)]
83pub struct TargetTextFunction(Function<TargetTextFunctionName, TargetTextParams>);
84
85#[derive(Parse, Peek, ToSpan, ToCursors, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
86#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
87pub struct TargetTextParams(TargetCounterKind, Option<T![,]>, Option<TextFunctionContent>);
88
89#[cfg(test)]
90mod tests {
91	use super::*;
92	use css_parse::{assert_parse, assert_parse_error};
93
94	#[test]
95	fn size_test() {
96		assert_eq!(std::mem::size_of::<Target>(), 200);
97	}
98
99	#[test]
100	fn test_writes() {
101		assert_parse!(Target, "target-counter('foo',bar,lower-roman)");
102		assert_parse!(Target, "target-counters('foo',bar,'baz',lower-roman)");
103		assert_parse!(Target, "target-text('foo')");
104		assert_parse!(Target, "target-text('foo',before)");
105	}
106
107	#[test]
108	fn test_errors() {
109		assert_parse_error!(Target, "target-counter()");
110		assert_parse_error!(Target, "target-counter('foo')");
111		assert_parse_error!(Target, "target-counters()");
112		assert_parse_error!(Target, "target-counters('foo')");
113		assert_parse_error!(Target, "target-counters('foo',bar)");
114		assert_parse_error!(Target, "target-text()");
115		assert_parse_error!(Target, "target-text(123)");
116	}
117}