css_ast/functions/
transform_functions.rs

1use crate::{AngleOrZero, Length, LengthPercentage, NoneOr, NumberOrPercentage};
2use css_parse::{Function, T, function_set};
3use csskit_derives::{Parse, Peek, ToCursors, ToSpan, Visitable};
4
5// https://drafts.csswg.org/css-transforms-1/#two-d-transform-functions
6#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
8#[allow(clippy::large_enum_variant)] // TODO: matrix3d should probably be boxed
9#[visit]
10pub enum TransformFunction {
11	Matrix(MatrixFunction),
12	Matrix3d(Matrix3dFunction),
13	Translate(TranslateFunction),
14	Translate3d(Translate3dFunction),
15	TranslateX(TranslatexFunction),
16	TranslateY(TranslateyFunction),
17	TranslateZ(TranslatezFunction),
18	Scale(ScaleFunction),
19	Scale3d(Scale3dFunction),
20	ScaleX(ScalexFunction),
21	ScaleY(ScaleyFunction),
22	ScaleZ(ScalezFunction),
23	Rotate(RotateFunction),
24	Rotate3d(Rotate3dFunction),
25	RotateX(RotatexFunction),
26	RotateY(RotateyFunction),
27	RotateZ(RotatezFunction),
28	Skew(SkewFunction),
29	SkewX(SkewxFunction),
30	SkewY(SkewyFunction),
31	Perspective(PerspectiveFunction),
32}
33
34function_set!(pub struct MatrixFunctionName "matrix");
35
36/// <https://drafts.csswg.org/css-transforms-1/#funcdef-transform-matrix>
37///
38/// ```text,ignore
39/// matrix() = matrix( <number>#{6} )
40/// ```
41#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
43#[visit(self)]
44pub struct MatrixFunction(pub Function<MatrixFunctionName, MatrixFunctionParams>);
45
46#[derive(Parse, Peek, ToCursors, ToSpan, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
48pub struct MatrixFunctionParams(
49	pub T![Number],
50	pub Option<T![,]>,
51	pub T![Number],
52	pub Option<T![,]>,
53	pub T![Number],
54	pub Option<T![,]>,
55	pub T![Number],
56	pub Option<T![,]>,
57	pub T![Number],
58	pub Option<T![,]>,
59	pub T![Number],
60);
61
62function_set!(pub struct Matrix3dFunctionName "matrix3d");
63
64/// <https://drafts.csswg.org/css-transforms-2/#funcdef-matrix3d>
65///
66/// ```text,ignore
67/// matrix3d() = matrix3d( <number>#{16} )
68/// ```
69#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
71#[visit(self)]
72pub struct Matrix3dFunction(pub Function<Matrix3dFunctionName, Matrix3dFunctionParams>);
73
74#[derive(Parse, Peek, ToCursors, ToSpan, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
75#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
76#[allow(clippy::type_complexity)] // TODO: simplify types
77pub struct Matrix3dFunctionParams(
78	pub T![Number],
79	pub Option<T![,]>,
80	pub T![Number],
81	pub Option<T![,]>,
82	pub T![Number],
83	pub Option<T![,]>,
84	pub T![Number],
85	pub Option<T![,]>,
86	pub T![Number],
87	pub Option<T![,]>,
88	pub T![Number],
89	pub Option<T![,]>,
90	pub T![Number],
91	pub Option<T![,]>,
92	pub T![Number],
93	pub Option<T![,]>,
94	pub T![Number],
95	pub Option<T![,]>,
96	pub T![Number],
97	pub Option<T![,]>,
98	pub T![Number],
99	pub Option<T![,]>,
100	pub T![Number],
101	pub Option<T![,]>,
102	pub T![Number],
103	pub Option<T![,]>,
104	pub T![Number],
105	pub Option<T![,]>,
106	pub T![Number],
107	pub Option<T![,]>,
108	pub T![Number],
109);
110
111function_set!(pub struct TranslateFunctionName "translate");
112
113/// <https://drafts.csswg.org/css-transforms-1/#funcdef-transform-translate>
114///
115/// ```text,ignore
116/// translate() = translate( <length-percentage> , <length-percentage>? )
117/// ```
118#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
119#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
120#[visit(self)]
121pub struct TranslateFunction(
122	pub Function<TranslateFunctionName, (LengthPercentage, Option<T![,]>, Option<LengthPercentage>)>,
123);
124
125function_set!(pub struct Translate3dFunctionName "translate3d");
126
127/// <https://drafts.csswg.org/css-transforms-2/#funcdef-translate3d>
128///
129/// ```text,ignore
130/// translate3d() = translate3d( <length-percentage> , <length-percentage> , <length> )
131/// ```
132#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
133#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
134#[visit(self)]
135pub struct Translate3dFunction(pub Function<Translate3dFunctionName, Translate3dFunctionParams>);
136
137#[derive(Parse, Peek, ToCursors, ToSpan, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
138#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
139pub struct Translate3dFunctionParams(
140	pub LengthPercentage,
141	pub Option<T![,]>,
142	pub LengthPercentage,
143	pub Option<T![,]>,
144	pub Length,
145);
146
147function_set!(pub struct TranslatexFunctionName "translatex");
148
149/// <https://drafts.csswg.org/css-transforms-1/#funcdef-transform-translatex>
150///
151/// ```text,ignore
152/// translateX() = translateX( <length-percentage> )
153/// ```
154#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
155#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
156#[visit(self)]
157pub struct TranslatexFunction(pub Function<TranslatexFunctionName, LengthPercentage>);
158
159function_set!(pub struct TranslateyFunctionName "translatey");
160
161/// <https://drafts.csswg.org/css-transforms-1/#funcdef-transform-translatey>
162///
163/// ```text,ignore
164/// translateY() = translateY( <length-percentage> )
165/// ```
166#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
167#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
168#[visit(self)]
169pub struct TranslateyFunction(pub Function<TranslateyFunctionName, LengthPercentage>);
170
171function_set!(pub struct TranslatezFunctionName "translatez");
172
173/// <https://drafts.csswg.org/css-transforms-2/#funcdef-translatez>
174///
175/// ```text,ignore
176/// translateZ() = translateZ( <length> )
177/// ```
178#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
179#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
180#[visit(self)]
181pub struct TranslatezFunction(pub Function<TranslatezFunctionName, Length>);
182
183function_set!(pub struct ScaleFunctionName "scale");
184
185/// <https://drafts.csswg.org/css-transforms-2/#funcdef-scale>
186///
187/// ```text,ignore
188/// scale() = scale( [ <number> | <percentage> ]#{1,2} )
189/// ```
190#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
191#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
192#[visit(self)]
193pub struct ScaleFunction(
194	pub Function<ScaleFunctionName, (NumberOrPercentage, Option<T![,]>, Option<NumberOrPercentage>)>,
195);
196
197function_set!(pub struct Scale3dFunctionName "scale3d");
198
199/// <https://drafts.csswg.org/css-transforms-2/#funcdef-scale3d>
200///
201/// ```text,ignore
202/// scale3d() = scale3d( [ <number> | <percentage> ]#{3} )
203/// ```
204#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
205#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
206#[visit(self)]
207pub struct Scale3dFunction(pub Function<Scale3dFunctionName, Scale3dFunctionParams>);
208
209#[derive(Parse, Peek, ToCursors, ToSpan, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
210#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
211pub struct Scale3dFunctionParams(
212	pub NumberOrPercentage,
213	pub Option<T![,]>,
214	pub NumberOrPercentage,
215	pub Option<T![,]>,
216	pub NumberOrPercentage,
217);
218
219function_set!(pub struct ScalexFunctionName "scalex");
220
221/// <https://drafts.csswg.org/css-transforms-2/#funcdef-scalex>
222///
223/// ```text,ignore
224/// scaleX() = scaleX( <number> | <percentage> )
225/// ````
226#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
227#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
228#[visit(self)]
229pub struct ScalexFunction(pub Function<ScalexFunctionName, NumberOrPercentage>);
230
231function_set!(pub struct ScaleyFunctionName "scaley");
232
233/// <https://drafts.csswg.org/css-transforms-2/#funcdef-scaley>
234///
235/// ```text,ignore
236/// scaleY() = scaleY( <number> | <percentage> )
237/// ````
238#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
239#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
240#[visit(self)]
241pub struct ScaleyFunction(pub Function<ScaleyFunctionName, NumberOrPercentage>);
242
243function_set!(pub struct ScalezFunctionName "scalez");
244
245/// <https://drafts.csswg.org/css-transforms-2/#funcdef-scalez>
246///
247/// ```text,ignore
248/// scaleZ() = scaleZ( <number> | <percentage> )
249/// ````
250#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
251#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
252#[visit(self)]
253pub struct ScalezFunction(pub Function<ScalezFunctionName, NumberOrPercentage>);
254
255function_set!(pub struct RotateFunctionName "rotate");
256
257/// <https://drafts.csswg.org/css-transforms-1/#funcdef-transform-rotate>
258///
259/// ```text,ignore
260/// rotate() = rotate( [ <angle> | <zero> ] )
261/// ```
262#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
263#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
264#[visit(self)]
265pub struct RotateFunction(pub Function<RotateFunctionName, AngleOrZero>);
266
267function_set!(pub struct Rotate3dFunctionName "rotate3d");
268
269/// <https://drafts.csswg.org/css-transforms-2/#funcdef-rotate3d>
270///
271/// ```text,ignore
272/// rotate3d() = rotate3d( <number> , <number> , <number> , [ <angle> | <zero> ] )
273/// ```
274#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
275#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
276#[visit(self)]
277pub struct Rotate3dFunction(pub Function<Rotate3dFunctionName, Rotate3dFunctionParams>);
278
279#[derive(Parse, Peek, ToCursors, ToSpan, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
280#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
281pub struct Rotate3dFunctionParams(
282	pub T![Number],
283	pub Option<T![,]>,
284	pub T![Number],
285	pub Option<T![,]>,
286	pub T![Number],
287	pub Option<T![,]>,
288	pub AngleOrZero,
289);
290
291function_set!(pub struct RotatexFunctionName "rotatex");
292
293/// <https://drafts.csswg.org/css-transforms-2/#funcdef-rotatex>
294///
295/// ```text,ignore
296/// rotateX() = rotateX( [ <angle> | <zero> ] )
297/// ```
298#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
299#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
300#[visit(self)]
301pub struct RotatexFunction(pub Function<RotatexFunctionName, AngleOrZero>);
302
303function_set!(pub struct RotateyFunctionName "rotatey");
304
305/// <https://drafts.csswg.org/css-transforms-2/#funcdef-rotatey>
306///
307/// ```text,ignore
308/// rotateY() = rotateY( [ <angle> | <zero> ] )
309/// ```
310#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
311#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
312#[visit(self)]
313pub struct RotateyFunction(pub Function<RotateyFunctionName, AngleOrZero>);
314
315function_set!(pub struct RotatezFunctionName "rotatez");
316
317/// <https://drafts.csswg.org/css-transforms-2/#funcdef-rotatez>
318///
319/// ```text,ignore
320/// rotateZ() = rotateZ( [ <angle> | <zero> ] )
321/// ```
322#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
323#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
324#[visit(self)]
325pub struct RotatezFunction(pub Function<RotatezFunctionName, AngleOrZero>);
326
327function_set!(pub struct SkewFunctionName "skew");
328
329/// <https://drafts.csswg.org/css-transforms-1/#funcdef-transform-skew>
330///
331/// ```text,ignore
332/// skew() = skew( [ <angle> | <zero> ] , [ <angle> | <zero> ]? )
333/// ```
334#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
335#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
336#[visit(self)]
337pub struct SkewFunction(pub Function<SkewFunctionName, (AngleOrZero, Option<T![,]>, Option<AngleOrZero>)>);
338
339function_set!(pub struct SkewxFunctionName "skewx");
340
341/// <https://drafts.csswg.org/css-transforms-1/#funcdef-transform-skewx>
342///
343/// ```text,ignore
344/// skewX() = skewX( [ <angle> | <zero> ] )
345/// ```
346#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
347#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
348#[visit(self)]
349pub struct SkewxFunction(pub Function<SkewxFunctionName, AngleOrZero>);
350
351function_set!(pub struct SkewyFunctionName "skewy");
352
353/// <https://drafts.csswg.org/css-transforms-1/#funcdef-transform-skewy>
354///
355/// ```text,ignore
356/// skewY() = skewY( [ <angle> | <zero> ] )
357/// ```
358#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
359#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
360#[visit(self)]
361pub struct SkewyFunction(pub Function<SkewyFunctionName, AngleOrZero>);
362
363function_set!(pub struct PerspectiveFunctionName "perspective");
364
365/// <https://drafts.csswg.org/css-transforms-2/#funcdef-perspective>
366///
367/// ```text,ignore
368/// perspective() = perspective( [ <length [0,∞]> | none ] )
369/// ```
370#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
371#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
372#[visit(self)]
373pub struct PerspectiveFunction(pub Function<PerspectiveFunctionName, NoneOr<Length>>);
374
375#[cfg(test)]
376mod tests {
377	use super::*;
378	use css_parse::{assert_parse, assert_parse_error, assert_parse_span};
379
380	#[test]
381	fn size_test() {
382		assert_eq!(std::mem::size_of::<TransformFunction>(), 460);
383	}
384
385	#[test]
386	fn test_writes() {
387		assert_parse!(TransformFunction, "matrix(1,2,3,4,5,6)");
388		assert_parse!(TransformFunction, "matrix(1 2 3 4 5 6)");
389		assert_parse!(TransformFunction, "matrix(0,0,0,0,0,0)");
390		assert_parse!(TransformFunction, "matrix(-1,-2,-3,-4,-5,-6)");
391		assert_parse!(TransformFunction, "matrix(1.5,2.5,3.5,4.5,5.5,6.5)");
392
393		assert_parse!(TransformFunction, "translate(10px)");
394		assert_parse!(TransformFunction, "translate(10px,20px)");
395		assert_parse!(TransformFunction, "translate(45%)");
396		assert_parse!(TransformFunction, "translate(2rem)");
397		assert_parse!(TransformFunction, "translateX(1rem)");
398		assert_parse!(TransformFunction, "translateY(1rem)");
399
400		assert_parse!(TransformFunction, "scale(1,2)");
401		assert_parse!(TransformFunction, "scale(0,0)");
402		assert_parse!(TransformFunction, "scale(1)");
403		assert_parse!(TransformFunction, "scale(1.5,2.5)");
404		assert_parse!(TransformFunction, "scaleX(2)");
405		assert_parse!(TransformFunction, "scaleY(2)");
406
407		assert_parse!(TransformFunction, "rotate(45deg)");
408		assert_parse!(TransformFunction, "rotate(0)");
409		assert_parse!(TransformFunction, "rotate(2turn)");
410		assert_parse!(TransformFunction, "rotate(20rad");
411
412		assert_parse!(TransformFunction, "skew(1deg,2deg)");
413		assert_parse!(TransformFunction, "skew(0,0)");
414		assert_parse!(TransformFunction, "skew(1deg)");
415		assert_parse!(TransformFunction, "skewX(1deg)");
416		assert_parse!(TransformFunction, "skewX(0)");
417		assert_parse!(TransformFunction, "skewY(1deg)");
418		assert_parse!(TransformFunction, "skewY(0)");
419
420		assert_parse!(TransformFunction, "scale3d(10%,10%,10%)");
421		assert_parse!(TransformFunction, "rotate3d(1,2,3,10deg)");
422	}
423
424	#[test]
425	fn test_span() {
426		assert_parse_span!(
427			TransformFunction,
428			r#"
429				matrix(1,2,3,4,5,6) translate(0)
430				^^^^^^^^^^^^^^^^^^^
431		"#
432		);
433		assert_parse_span!(
434			TransformFunction,
435			r#"
436				translate(0) foo
437				^^^^^^^^^^^^
438		"#
439		);
440		assert_parse_span!(
441			TranslateFunction,
442			r#"
443				translate(0) bar
444				^^^^^^^^^^^^
445		"#
446		);
447	}
448
449	#[test]
450	fn test_errors() {
451		assert_parse_error!(TransformFunction, "matrix()");
452		assert_parse_error!(TransformFunction, "matrix(1)");
453		assert_parse_error!(TransformFunction, "matrix(1,2)");
454		assert_parse_error!(TransformFunction, "matrix(one,two,three,four,five,size)");
455
456		assert_parse_error!(TransformFunction, "translate()");
457		assert_parse_error!(TransformFunction, "translate(foo)");
458		assert_parse_error!(TransformFunction, "translateX()");
459		assert_parse_error!(TransformFunction, "translateX(foo)");
460		assert_parse_error!(TransformFunction, "translateY()");
461		assert_parse_error!(TransformFunction, "translateY(foo)");
462
463		assert_parse_error!(TransformFunction, "scale()");
464		assert_parse_error!(TransformFunction, "scale(foo)");
465		assert_parse_error!(TransformFunction, "scaleX()");
466		assert_parse_error!(TransformFunction, "scaleX(foo)");
467		assert_parse_error!(TransformFunction, "scaleY()");
468		assert_parse_error!(TransformFunction, "scaleY(foo)");
469
470		assert_parse_error!(TransformFunction, "rotate()");
471		assert_parse_error!(TransformFunction, "rotate(45px)");
472		assert_parse_error!(TransformFunction, "rotate(all the way around)");
473
474		assert_parse_error!(TransformFunction, "skew()");
475		assert_parse_error!(TransformFunction, "skew(foo)");
476		assert_parse_error!(TransformFunction, "skewX()");
477		assert_parse_error!(TransformFunction, "skewX(foo)");
478		assert_parse_error!(TransformFunction, "skewY()");
479		assert_parse_error!(TransformFunction, "skewY(foo)");
480	}
481}