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#[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#[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#[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#[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#[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#[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 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 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}