1use crate::diagnostics;
2use css_parse::{
3 Build, CommaSeparated, Cursor, Function, Parse, Parser, Peek, Result as ParserResult, T, function_set, keyword_set,
4};
5use csskit_derives::{Parse, Peek, ToCursors, ToSpan, Visitable};
6
7use crate::CSSInt;
8
9function_set!(
10 pub enum EasingFunctionName {
11 Linear: "linear",
12 CubicBezier: "cubic-bezier",
13 Steps: "steps"
14 }
15);
16
17keyword_set!(
18 pub enum EasingKeyword {
19 Linear: "linear",
20 Ease: "ease",
21 EaseIn: "ease-in",
22 EaseOut: "ease-out",
23 EaseInOut: "ease-in-out",
24 StepStart: "step-start",
25 StepEnd: "step-end",
26 }
27);
28
29keyword_set!(
30 pub enum StepPosition {
31 JumpStart: "jump-start",
32 JumpEnd: "jump-end",
33 JumpNone: "jump-none",
34 JumpBoth: "jump-both",
35 Start: "start",
36 End: "end",
37 }
38);
39
40#[derive(ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
61#[visit]
62pub enum EasingFunction<'a> {
63 #[visit(skip)]
64 Linear(T![Ident]),
65 #[visit(skip)]
66 Ease(T![Ident]),
67 #[visit(skip)]
68 EaseIn(T![Ident]),
69 #[visit(skip)]
70 EaseOut(T![Ident]),
71 #[visit(skip)]
72 EaseInOut(T![Ident]),
73 #[visit(skip)]
74 StepStart(T![Ident]),
75 #[visit(skip)]
76 StepEnd(T![Ident]),
77 LinearFunction(LinearFunction<'a>),
78 CubicBezierFunction(CubicBezierFunction),
79 StepsFunction(StepsFunction),
80}
81
82impl<'a> Peek<'a> for EasingFunction<'a> {
83 fn peek(p: &Parser<'a>, c: Cursor) -> bool {
84 EasingKeyword::peek(p, c) || EasingFunctionName::peek(p, c)
85 }
86}
87
88impl<'a> Parse<'a> for EasingFunction<'a> {
89 fn parse(p: &mut Parser<'a>) -> ParserResult<Self> {
90 if p.peek::<EasingKeyword>() {
91 let keyword = p.parse::<EasingKeyword>()?;
92 let c = keyword.into();
93 let ident = <T![Ident]>::build(p, c);
94 return match keyword {
95 EasingKeyword::Linear(_) => Ok(Self::Linear(ident)),
96 EasingKeyword::Ease(_) => Ok(Self::Ease(ident)),
97 EasingKeyword::EaseIn(_) => Ok(Self::EaseIn(ident)),
98 EasingKeyword::EaseOut(_) => Ok(Self::EaseOut(ident)),
99 EasingKeyword::EaseInOut(_) => Ok(Self::EaseInOut(ident)),
100 EasingKeyword::StepStart(_) => Ok(Self::StepStart(ident)),
101 EasingKeyword::StepEnd(_) => Ok(Self::StepEnd(ident)),
102 };
103 }
104 let c = p.peek_n(1);
105 let easing_function = if EasingFunctionName::peek(p, c) { Some(EasingFunctionName::build(p, c)) } else { None };
106 match easing_function {
107 Some(EasingFunctionName::Linear(_)) => p.parse::<LinearFunction>().map(Self::LinearFunction),
108 Some(EasingFunctionName::CubicBezier(_)) => p.parse::<CubicBezierFunction>().map(Self::CubicBezierFunction),
109 Some(EasingFunctionName::Steps(_)) => p.parse::<StepsFunction>().map(Self::StepsFunction),
110 None => Err(diagnostics::Unexpected(p.next()))?,
111 }
112 }
113}
114
115function_set!(pub struct LinearFunctionName "linear");
116
117#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
118#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
119#[visit(self)]
120pub struct LinearFunction<'a>(Function<LinearFunctionName, CommaSeparated<'a, LinearFunctionParams>>);
121
122#[derive(Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
123#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
124#[visit(self)]
125pub struct LinearFunctionParams(T![Number], Option<T![Dimension::%]>, Option<T![Dimension::%]>);
126
127impl<'a> Parse<'a> for LinearFunctionParams {
128 fn parse(p: &mut Parser<'a>) -> ParserResult<Self> {
129 let mut num = p.parse_if_peek::<T![Number]>()?;
130 let percent = p.parse_if_peek::<T![Dimension::%]>()?;
131 let percent2 = p.parse_if_peek::<T![Dimension::%]>()?;
132 if num.is_none() {
133 num = Some(p.parse::<T![Number]>()?);
134 }
135 Ok(Self(num.unwrap(), percent, percent2))
136 }
137}
138
139function_set!(pub struct CubicBezierFunctionName "cubic-bezier");
140
141#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
142#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
143#[visit(self)]
144pub struct CubicBezierFunction(Function<CubicBezierFunctionName, CubicBezierFunctionParams>);
145
146#[derive(Parse, Peek, ToCursors, ToSpan, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
147#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
148pub struct CubicBezierFunctionParams {
149 x1: T![Number],
150 c1: Option<T![,]>,
151 x2: T![Number],
152 c2: Option<T![,]>,
153 y1: T![Number],
154 c3: Option<T![,]>,
155 y2: T![Number],
156}
157
158#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
159#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
160#[visit(self)]
161pub struct StepsFunction(Function<EasingFunctionName, StepsFunctionParams>);
162
163#[derive(Parse, Peek, ToCursors, ToSpan, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
164#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
165pub struct StepsFunctionParams(CSSInt, Option<T![,]>, Option<StepPosition>);
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use css_parse::{assert_parse, assert_parse_error};
171
172 #[test]
173 fn size_test() {
174 assert_eq!(std::mem::size_of::<EasingFunction>(), 128);
175 }
176
177 #[test]
178 fn test_writes() {
179 assert_parse!(EasingFunction, "ease-in-out");
180 assert_parse!(EasingFunction, "linear(0,1)");
181 assert_parse!(EasingFunction, "linear(0,0.25,1)");
182 assert_parse!(EasingFunction, "linear(0,0.5 25% 75%,1)");
183 assert_parse!(EasingFunction, "cubic-bezier(0.25,0.1,0.25,1)");
184 assert_parse!(EasingFunction, "cubic-bezier(0.1,-0.6,0.2,0)");
185 assert_parse!(EasingFunction, "cubic-bezier(0,0,1,1)");
186 assert_parse!(EasingFunction, "steps(4,end)");
187 assert_parse!(EasingFunction, "steps(10,jump-both)");
188 assert_parse!(EasingFunction, "linear(0,0.25,1");
190 assert_parse!(EasingFunction, "cubic-bezier(0.1 -0.6 0.2 0)");
191 }
192
193 #[test]
194 fn test_errors() {
195 assert_parse_error!(EasingFunction, "foo");
196 assert_parse_error!(EasingFunction, "linear()");
197 assert_parse_error!(EasingFunction, "cubic-bezier(0.1, red, 1.0, green)");
198 }
199}