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