Skip to main content

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