css_ast/functions/
easing_functions.rs

1use super::prelude::*;
2use crate::Percentage;
3
4// https://drafts.csswg.org/css-easing-2/#typedef-easing-function
5// <easing-function> = <linear-easing-function>
6//                      | <cubic-bezier-easing-function>
7//                      | <step-easing-function>
8//
9// <linear-easing-function> = linear | <linear()>
10//
11// linear() = linear( [ <number> && <percentage>{0,2} ]# )
12//
13// <cubic-bezier-easing-function> =
14// 	ease | ease-in | ease-out | ease-in-out | <cubic-bezier()>
15//
16// cubic-bezier() = cubic-bezier( [ <number [0,1]>, <number> ]#{2} )
17//
18// <step-easing-function> = step-start | step-end | <steps()>
19//
20// steps() = steps( <integer>, <step-position>?)
21//
22// <step-position> = jump-start | jump-end | jump-none | jump-both | start | end
23#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
25#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
26#[derive(csskit_derives::NodeWithMetadata)]
27pub enum EasingFunction<'a> {
28	#[cfg_attr(feature = "visitable", visit(skip))]
29	#[atom(CssAtomSet::Linear)]
30	Linear(T![Ident]),
31	#[cfg_attr(feature = "visitable", visit(skip))]
32	#[atom(CssAtomSet::Ease)]
33	Ease(T![Ident]),
34	#[cfg_attr(feature = "visitable", visit(skip))]
35	#[atom(CssAtomSet::EaseIn)]
36	EaseIn(T![Ident]),
37	#[cfg_attr(feature = "visitable", visit(skip))]
38	#[atom(CssAtomSet::EaseOut)]
39	EaseOut(T![Ident]),
40	#[cfg_attr(feature = "visitable", visit(skip))]
41	#[atom(CssAtomSet::EaseInOut)]
42	EaseInOut(T![Ident]),
43	#[cfg_attr(feature = "visitable", visit(skip))]
44	#[atom(CssAtomSet::StepStart)]
45	StepStart(T![Ident]),
46	#[cfg_attr(feature = "visitable", visit(skip))]
47	#[atom(CssAtomSet::StepEnd)]
48	StepEnd(T![Ident]),
49	#[atom(CssAtomSet::Linear)]
50	LinearFunction(LinearFunction<'a>),
51	#[atom(CssAtomSet::CubicBezier)]
52	CubicBezierFunction(CubicBezierFunction),
53	#[atom(CssAtomSet::Steps)]
54	StepsFunction(StepsFunction),
55}
56
57#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
59#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
60#[derive(csskit_derives::NodeWithMetadata)]
61pub struct LinearFunction<'a> {
62	#[atom(CssAtomSet::Linear)]
63	pub name: T![Function],
64	pub params: CommaSeparated<'a, LinearFunctionParams>,
65	pub close: T![')'],
66}
67
68#[derive(Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
70#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
71#[derive(csskit_derives::NodeWithMetadata)]
72pub struct LinearFunctionParams(T![Number], Option<Percentage>, Option<Percentage>);
73
74impl<'a> Parse<'a> for LinearFunctionParams {
75	fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
76	where
77		I: Iterator<Item = Cursor> + Clone,
78	{
79		let mut num = p.parse_if_peek::<T![Number]>()?;
80		let percent = p.parse_if_peek::<Percentage>()?;
81		let percent2 = p.parse_if_peek::<Percentage>()?;
82		if num.is_none() {
83			num = Some(p.parse::<T![Number]>()?);
84		}
85		Ok(Self(num.unwrap(), percent, percent2))
86	}
87}
88
89#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
91#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
92#[derive(csskit_derives::NodeWithMetadata)]
93pub struct CubicBezierFunction {
94	#[atom(CssAtomSet::CubicBezier)]
95	pub name: T![Function],
96	pub params: CubicBezierFunctionParams,
97	pub close: T![')'],
98}
99
100#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
101#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
102pub struct CubicBezierFunctionParams {
103	x1: T![Number],
104	c1: Option<T![,]>,
105	x2: T![Number],
106	c2: Option<T![,]>,
107	y1: T![Number],
108	c3: Option<T![,]>,
109	y2: T![Number],
110}
111
112#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
114#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
115#[derive(csskit_derives::NodeWithMetadata)]
116pub struct StepsFunction {
117	#[atom(CssAtomSet::Steps)]
118	pub name: T![Function],
119	pub params: StepsFunctionParams,
120	pub close: T![')'],
121}
122
123#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
124#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
125pub struct StepsFunctionParams(CSSInt, Option<T![,]>, Option<StepPosition>);
126
127#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
128#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
129pub enum StepPosition {
130	#[atom(CssAtomSet::JumpStart)]
131	JumpStart(T![Ident]),
132	#[atom(CssAtomSet::JumpEnd)]
133	JumpEnd(T![Ident]),
134	#[atom(CssAtomSet::JumpNone)]
135	JumpNone(T![Ident]),
136	#[atom(CssAtomSet::JumpBoth)]
137	JumpBoth(T![Ident]),
138	#[atom(CssAtomSet::Start)]
139	Start(T![Ident]),
140	#[atom(CssAtomSet::End)]
141	End(T![Ident]),
142}
143
144#[cfg(test)]
145mod tests {
146	use super::*;
147	use crate::CssAtomSet;
148	use css_parse::{assert_parse, assert_parse_error};
149
150	#[test]
151	fn size_test() {
152		assert_eq!(std::mem::size_of::<EasingFunction>(), 120);
153	}
154
155	#[test]
156	fn test_writes() {
157		assert_parse!(CssAtomSet::ATOMS, EasingFunction, "ease-in-out");
158		assert_parse!(CssAtomSet::ATOMS, EasingFunction, "linear(0,1)");
159		assert_parse!(CssAtomSet::ATOMS, EasingFunction, "linear(0,0.25,1)");
160		assert_parse!(CssAtomSet::ATOMS, EasingFunction, "linear(0,0.5 25% 75%,1)");
161		assert_parse!(CssAtomSet::ATOMS, EasingFunction, "cubic-bezier(0.25,0.1,0.25,1)");
162		assert_parse!(CssAtomSet::ATOMS, EasingFunction, "cubic-bezier(0.1,-0.6,0.2,0)");
163		assert_parse!(CssAtomSet::ATOMS, EasingFunction, "cubic-bezier(0,0,1,1)");
164		assert_parse!(CssAtomSet::ATOMS, EasingFunction, "steps(4,end)");
165		assert_parse!(CssAtomSet::ATOMS, EasingFunction, "steps(10,jump-both)");
166		assert_parse!(CssAtomSet::ATOMS, EasingFunction, "linear(0,0.25,1)");
167		assert_parse!(CssAtomSet::ATOMS, EasingFunction, "cubic-bezier(0.1 -0.6 0.2 0)");
168	}
169
170	#[test]
171	fn test_errors() {
172		assert_parse_error!(CssAtomSet::ATOMS, EasingFunction, "foo");
173		assert_parse_error!(CssAtomSet::ATOMS, EasingFunction, "linear()");
174		assert_parse_error!(CssAtomSet::ATOMS, EasingFunction, "cubic-bezier(0.1, red, 1.0, green)");
175	}
176}