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