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