css_ast/functions/
gradient_functions.rs

1use super::prelude::*;
2use crate::{Length, LengthPercentage};
3
4/// <https://drafts.csswg.org/css-images-3/#typedef-gradient>
5/// ```text-ignore,
6/// <gradient> = <linear-gradient()> | <repeating-linear-gradient()> | <radial-gradient()> | <repeating-radial-gradient()>
7/// ```
8#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
10#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
11#[derive(csskit_derives::NodeWithMetadata)]
12pub enum Gradient<'a> {
13	#[atom(CssAtomSet::LinearGradient)]
14	LinearGradientFunction(LinearGradientFunction<'a>),
15	#[atom(CssAtomSet::RepeatingLinearGradient)]
16	RepeatingLinearGradientFunction(RepeatingLinearGradientFunction<'a>),
17	#[atom(CssAtomSet::RadialGradient)]
18	RadialGradientFunction(RadialGradientFunction<'a>),
19	#[atom(CssAtomSet::RepeatingRadialGradient)]
20	RepeatingRadialGradientFunction(RepeatingRadialGradientFunction<'a>),
21}
22
23/// <https://drafts.csswg.org/css-images-3/#funcdef-linear-gradient>
24/// ```text,ignore
25/// <linear-gradient()> = linear-gradient( [ <linear-gradient-syntax> ] )
26/// <linear-gradient-syntax> = [ <angle> | <zero> | to <side-or-corner> ]? , <color-stop-list>
27/// <side-or-corner> = [left | right] || [top | bottom]
28/// ```
29#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
31#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
32#[derive(csskit_derives::NodeWithMetadata)]
33pub struct LinearGradientFunction<'a> {
34	#[atom(CssAtomSet::LinearGradient)]
35	pub name: T![Function],
36	pub params: LinearGradientFunctionParams<'a>,
37	pub close: T![')'],
38}
39
40#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
42pub struct LinearGradientFunctionParams<'a>(
43	Option<LinearDirection>,
44	Option<T![,]>,
45	CommaSeparated<'a, ColorStopOrHint>,
46);
47
48/// <https://drafts.csswg.org/css-images-3/#funcdef-repeating-linear-gradient>
49/// ```text,ignore
50/// <repeating-linear-gradient()> = repeating-linear-gradient( [ <linear-gradient-syntax> ] )
51/// <linear-gradient-syntax> = [ <angle> | <zero> | to <side-or-corner> ]? , <color-stop-list>
52/// <side-or-corner> = [left | right] || [top | bottom]
53/// ```
54#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
55#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
56#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
57#[derive(csskit_derives::NodeWithMetadata)]
58pub struct RepeatingLinearGradientFunction<'a> {
59	#[atom(CssAtomSet::RepeatingLinearGradient)]
60	pub name: T![Function],
61	pub params: RepeatingLinearGradientFunctionParams<'a>,
62	pub close: T![')'],
63}
64
65#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
66#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
67pub struct RepeatingLinearGradientFunctionParams<'a>(
68	Option<LinearDirection>,
69	Option<T![,]>,
70	CommaSeparated<'a, ColorStopOrHint>,
71);
72
73/// <https://drafts.csswg.org/css-images-3/#funcdef-radial-gradient>
74/// ```text,ignore
75/// <radial-gradient()> = radial-gradient( [ <radial-gradient-syntax> ] )
76/// <radial-gradient-syntax> = [ <radial-shape> || <radial-size> ]? [ at <position> ]? , <color-stop-list>
77/// <radial-size> = <radial-extent> | <length [0,∞]> | <length-percentage [0,∞]>{2}
78/// <radial-extent> = closest-corner | closest-side | farthest-corner | farthest-side
79/// <radial-shape> = circle | ellipse
80/// ```
81#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
83#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
84#[derive(csskit_derives::NodeWithMetadata)]
85pub struct RadialGradientFunction<'a> {
86	#[atom(CssAtomSet::RadialGradient)]
87	pub name: T![Function],
88	pub params: RadialGradientFunctionParams<'a>,
89	pub close: T![')'],
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())]
94pub struct RadialGradientFunctionParams<'a>(
95	Option<RadialSize>,
96	Option<RadialShape>,
97	Option<T![Ident]>,
98	Option<Position>,
99	Option<T![,]>,
100	CommaSeparated<'a, ColorStopOrHint>,
101);
102
103/// <https://drafts.csswg.org/css-images-3/#funcdef-repeating-radial-gradient>
104/// ```text,ignore
105/// <repeating-radial-gradient()> = repeating-radial-gradient( [ <radial-gradient-syntax> ] )
106/// <radial-gradient-syntax> = [ <radial-shape> || <radial-size> ]? [ at <position> ]? , <color-stop-list>
107/// <radial-size> = <radial-extent> | <length [0,∞]> | <length-percentage [0,∞]>{2}
108/// <radial-extent> = closest-corner | closest-side | farthest-corner | farthest-side
109/// <radial-shape> = circle | ellipse
110/// ```
111#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
112#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
113#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
114#[derive(csskit_derives::NodeWithMetadata)]
115pub struct RepeatingRadialGradientFunction<'a> {
116	#[atom(CssAtomSet::RepeatingRadialGradient)]
117	pub name: T![Function],
118	pub params: RepeatingRadialGradientFunctionParams<'a>,
119	pub close: T![')'],
120}
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 struct RepeatingRadialGradientFunctionParams<'a>(
125	Option<RadialSize>,
126	Option<RadialShape>,
127	Option<T![Ident]>,
128	Option<Position>,
129	Option<T![,]>,
130	CommaSeparated<'a, ColorStopOrHint>,
131);
132
133#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable))]
134#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
136#[cfg_attr(feature = "visitable", visit(skip))]
137pub enum NamedDirection {
138	#[atom(CssAtomSet::Bottom)]
139	Bottom(T![Ident]),
140	#[atom(CssAtomSet::Top)]
141	Top(T![Ident]),
142	#[atom(CssAtomSet::Left)]
143	Left(T![Ident]),
144	#[atom(CssAtomSet::Right)]
145	Right(T![Ident]),
146}
147
148#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
149#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
150pub enum LinearDirection {
151	Angle(Angle),
152	Named(#[atom(CssAtomSet::To)] T![Ident], NamedDirection, Option<NamedDirection>),
153}
154
155/// <https://drafts.csswg.org/css-images-3/#typedef-radial-size>
156///
157/// ```text,ignore
158/// <radial-size> = <radial-extent> | <length [0,∞]> | <length-percentage [0,∞]>{2}
159/// <radial-extent> = closest-corner | closest-side | farthest-corner | farthest-side
160/// ```
161#[derive(Peek, ToSpan, ToCursors, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
162#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
163pub enum RadialSize {
164	Extent(RadialExtent),
165	Circular(Length),
166	Elliptical(LengthPercentage, LengthPercentage),
167}
168
169/// <https://drafts.csswg.org/css-images-3/#typedef-radial-extent>
170///
171/// ```text,ignore
172/// <radial-extent> = closest-corner | closest-side | farthest-corner | farthest-side
173/// ```
174#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable))]
175#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
176#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
177#[cfg_attr(feature = "visitable", visit(skip))]
178pub enum RadialExtent {
179	#[atom(CssAtomSet::ClosestCorner)]
180	ClosestCorner(T![Ident]),
181	#[atom(CssAtomSet::ClosestSide)]
182	ClosestSide(T![Ident]),
183	#[atom(CssAtomSet::FarthestCorner)]
184	FarthestCorner(T![Ident]),
185	#[atom(CssAtomSet::FarthestSide)]
186	FarthestSide(T![Ident]),
187}
188
189impl<'a> Parse<'a> for RadialSize {
190	fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
191	where
192		I: Iterator<Item = Cursor> + Clone,
193	{
194		if let Some(extent) = p.parse_if_peek::<RadialExtent>()? {
195			return Ok(RadialSize::Extent(extent));
196		}
197		if p.peek::<Length>() {
198			let first_len = p.parse::<LengthPercentage>()?;
199			if !p.peek::<Length>()
200				&& let LengthPercentage::Length(len) = first_len
201			{
202				return Ok(Self::Circular(len));
203			}
204			let second_len = p.parse::<LengthPercentage>()?;
205			return Ok(Self::Elliptical(first_len, second_len));
206		}
207		let first = p.parse::<LengthPercentage>()?;
208		let second = p.parse::<LengthPercentage>()?;
209		Ok(Self::Elliptical(first, second))
210	}
211}
212
213/// <https://drafts.csswg.org/css-images-3/#typedef-radial-shape>
214///
215/// ```text,ignore
216/// <radial-shape> = circle | ellipse
217/// ```
218#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable))]
219#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
220#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
221#[cfg_attr(feature = "visitable", visit(skip))]
222pub enum RadialShape {
223	#[atom(CssAtomSet::Circle)]
224	Circle(T![Ident]),
225	#[atom(CssAtomSet::Ellipse)]
226	Ellipse(T![Ident]),
227}
228
229#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
230#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
231pub enum ColorStopOrHint {
232	Hint(LengthPercentage),
233	Stop(Color, Option<LengthPercentage>),
234}
235
236#[cfg(test)]
237mod tests {
238	use super::*;
239	use crate::CssAtomSet;
240	use css_parse::assert_parse;
241
242	#[test]
243	fn size_test() {
244		assert_eq!(std::mem::size_of::<Gradient>(), 208);
245		assert_eq!(std::mem::size_of::<LinearDirection>(), 44);
246		assert_eq!(std::mem::size_of::<RadialSize>(), 32);
247		assert_eq!(std::mem::size_of::<ColorStopOrHint>(), 156);
248	}
249
250	#[test]
251	fn test_writes() {
252		assert_parse!(CssAtomSet::ATOMS, Gradient, "linear-gradient(to bottom,yellow,blue)");
253		assert_parse!(CssAtomSet::ATOMS, Gradient, "linear-gradient(yellow,blue)");
254		assert_parse!(CssAtomSet::ATOMS, Gradient, "linear-gradient(to bottom,#fff,#fff 85%,#e6e6e6)");
255		assert_parse!(CssAtomSet::ATOMS, Gradient, "linear-gradient(45deg,#808080 25%,transparent 25%)");
256		assert_parse!(CssAtomSet::ATOMS, Gradient, "linear-gradient(to right,transparent,red 20%,red 80%,transparent)");
257		assert_parse!(
258			CssAtomSet::ATOMS,
259			Gradient,
260			"radial-gradient(closest-corner circle,rgba(1,65,255,0.4),rgba(1,65,255,0))"
261		);
262	}
263}