Skip to main content

css_ast/functions/
target_functions.rs

1use super::prelude::*;
2use crate::{CssAtomSet, types::CounterStyle};
3use css_parse::Box;
4
5#[node]
6#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
8#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(skip))]
9#[derive(csskit_derives::NodeWithMetadata)]
10pub enum TextFunctionContent {
11	#[atom(CssAtomSet::Content)]
12	Content(T![Ident]),
13	#[atom(CssAtomSet::Before)]
14	Before(T![Ident]),
15	#[atom(CssAtomSet::After)]
16	After(T![Ident]),
17	#[atom(CssAtomSet::FirstLetter)]
18	FirstLetter(T![Ident]),
19}
20
21#[derive(
22	Parse, Peek, ToCursors, IntoCursor, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
23)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
25#[derive(csskit_derives::NodeWithMetadata)]
26pub enum TargetCounterKind {
27	String(T![String]),
28	Url(T![Url]),
29}
30
31/// <https://drafts.csswg.org/css-content-3/#typedef-target>
32///
33/// ```text,ignore
34/// <target> = <target-counter()> | <target-counters()> | <target-text()>
35/// ```
36#[node]
37#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
39#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
40#[derive(csskit_derives::NodeWithMetadata)]
41pub enum Target<'a> {
42	/// <https://drafts.csswg.org/css-content-3/#target-counter>
43	///
44	/// ```text,ignore
45	/// target-counter() = target-counter( [ <string> | <url> ] , <custom-ident> , <counter-style>? )
46	/// ```
47	TargetCounter(Box<'a, TargetCounterFunction<'a>>),
48	/// <https://drafts.csswg.org/css-content-3/#target-counters>
49	///
50	/// ```text,ignore
51	/// target-counters() = target-counters( [ <string> | <url> ] , <custom-ident> , <string> , <counter-style>? )
52	/// ```
53	TargetCounters(Box<'a, TargetCountersFunction<'a>>),
54	/// <https://drafts.csswg.org/css-content-3/#target-text>
55	///
56	/// ```text,ignore
57	/// target-text() = target-text( [ <string> | <url> ] , [ content | before | after | first-letter ]? )
58	/// ```
59	TargetText(TargetTextFunction),
60}
61
62#[node]
63#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
64#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
65#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
66#[derive(csskit_derives::NodeWithMetadata)]
67pub struct TargetCounterFunction<'a> {
68	#[atom(CssAtomSet::TargetCounter)]
69	pub name: T![Function],
70	pub params: TargetCounterParams<'a>,
71	#[semantic_eq(skip)]
72	pub close: T![')'],
73}
74
75#[node]
76#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
77#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
78#[derive(csskit_derives::NodeWithMetadata)]
79pub struct TargetCounterParams<'a>(
80	TargetCounterKind,
81	#[semantic_eq(skip)] Option<T![,]>,
82	T![Ident],
83	#[semantic_eq(skip)] Option<T![,]>,
84	Option<CounterStyle<'a>>,
85);
86
87#[node]
88#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
89#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
90#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
91#[derive(csskit_derives::NodeWithMetadata)]
92pub struct TargetCountersFunction<'a> {
93	#[atom(CssAtomSet::TargetCounters)]
94	pub name: T![Function],
95	pub params: TargetCountersParams<'a>,
96	#[semantic_eq(skip)]
97	pub close: T![')'],
98}
99
100#[node]
101#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
102#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
103#[derive(csskit_derives::NodeWithMetadata)]
104pub struct TargetCountersParams<'a>(
105	TargetCounterKind,
106	#[semantic_eq(skip)] Option<T![,]>,
107	T![Ident],
108	#[semantic_eq(skip)] Option<T![,]>,
109	T![String],
110	#[semantic_eq(skip)] Option<T![,]>,
111	Option<CounterStyle<'a>>,
112);
113
114#[node]
115#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
117#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
118#[derive(csskit_derives::NodeWithMetadata)]
119pub struct TargetTextFunction {
120	#[atom(CssAtomSet::TargetText)]
121	pub name: T![Function],
122	pub params: TargetTextParams,
123	#[semantic_eq(skip)]
124	pub close: T![')'],
125}
126
127#[node]
128#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
129#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
130#[derive(csskit_derives::NodeWithMetadata)]
131pub struct TargetTextParams(TargetCounterKind, #[semantic_eq(skip)] Option<T![,]>, Option<TextFunctionContent>);
132
133#[cfg(test)]
134mod tests {
135	use super::*;
136	use crate::CssAtomSet;
137	use css_parse::{assert_parse, assert_parse_error};
138
139	#[test]
140	fn test_writes() {
141		assert_parse!(CssAtomSet::ATOMS, Target, "target-counter('foo',bar,lower-roman)");
142		assert_parse!(CssAtomSet::ATOMS, Target, "target-counters('foo',bar,'baz',lower-roman)");
143		assert_parse!(CssAtomSet::ATOMS, Target, "target-text('foo')");
144		assert_parse!(CssAtomSet::ATOMS, Target, "target-text('foo',before)");
145	}
146
147	#[test]
148	fn test_errors() {
149		assert_parse_error!(CssAtomSet::ATOMS, Target, "target-counter()");
150		assert_parse_error!(CssAtomSet::ATOMS, Target, "target-counter('foo')");
151		assert_parse_error!(CssAtomSet::ATOMS, Target, "target-counters()");
152		assert_parse_error!(CssAtomSet::ATOMS, Target, "target-counters('foo')");
153		assert_parse_error!(CssAtomSet::ATOMS, Target, "target-counters('foo',bar)");
154		assert_parse_error!(CssAtomSet::ATOMS, Target, "target-text()");
155		assert_parse_error!(CssAtomSet::ATOMS, Target, "target-text(123)");
156	}
157}