css_ast/functions/
gradient_functions.rs

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