1use super::prelude::*;
2use crate::{AngleOrZero, CalcableValue, Color, Length, NonNegative, NumberOrPercentage, NumericValue, Url};
3
4#[node]
12#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
14#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
15#[derive(csskit_derives::NodeWithMetadata)]
16pub enum FilterFunction<'a> {
17 #[atom(CssAtomSet::Blur)]
18 Blur(BlurFunction<'a>),
19 #[atom(CssAtomSet::Brightness)]
20 Brightness(BrightnessFunction<'a>),
21 #[atom(CssAtomSet::Contrast)]
22 Contrast(ContrastFunction<'a>),
23 #[atom(CssAtomSet::DropShadow)]
24 DropShadow(DropShadowFunction<'a>),
25 #[atom(CssAtomSet::Grayscale)]
26 Grayscale(GrayscaleFunction<'a>),
27 #[atom(CssAtomSet::HueRotate)]
28 HueRotate(HueRotateFunction<'a>),
29 #[atom(CssAtomSet::Invert)]
30 Invert(InvertFunction<'a>),
31 #[atom(CssAtomSet::Opacity)]
32 Opacity(OpacityFunction<'a>),
33 #[atom(CssAtomSet::Saturate)]
34 Saturate(SaturateFunction<'a>),
35 #[atom(CssAtomSet::Sepia)]
36 Sepia(SepiaFunction<'a>),
37}
38
39#[node]
45#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
47#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
48#[derive(csskit_derives::NodeWithMetadata)]
49pub struct FilterValueList<'a>(pub Vec<'a, FilterValue<'a>>);
50
51#[node]
53#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
55#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
56#[derive(csskit_derives::NodeWithMetadata)]
57pub enum FilterValue<'a> {
58 FilterFunction(FilterFunction<'a>),
59 Url(Url),
60}
61
62#[node]
64#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
65#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
66#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
67#[derive(csskit_derives::NodeWithMetadata)]
68pub struct BlurFunction<'a> {
69 #[atom(CssAtomSet::Blur)]
70 pub name: T![Function],
71 pub radius: Option<CalcableValue<'a, NonNegative<Length>>>,
72 #[semantic_eq(skip)]
73 pub close: T![')'],
74}
75
76#[node]
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))]
81#[derive(csskit_derives::NodeWithMetadata)]
82pub struct BrightnessFunction<'a> {
83 #[atom(CssAtomSet::Brightness)]
84 pub name: T![Function],
85 pub value: Option<NumericValue<'a, NumberOrPercentage>>,
86 #[semantic_eq(skip)]
87 pub close: T![')'],
88}
89
90#[node]
92#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
93#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
94#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
95#[derive(csskit_derives::NodeWithMetadata)]
96pub struct ContrastFunction<'a> {
97 #[atom(CssAtomSet::Contrast)]
98 pub name: T![Function],
99 pub value: Option<NumericValue<'a, NumberOrPercentage>>,
100 #[semantic_eq(skip)]
101 pub close: T![')'],
102}
103
104#[node]
108#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
109#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
110#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
111#[derive(csskit_derives::NodeWithMetadata)]
112pub struct DropShadowFunction<'a> {
113 #[atom(CssAtomSet::DropShadow)]
114 pub name: T![Function],
115 pub color: Option<Color<'a>>,
116 pub offset_x: CalcableValue<'a, Length>,
117 pub offset_y: CalcableValue<'a, Length>,
118 pub blur_radius: Option<CalcableValue<'a, NonNegative<Length>>>,
119 #[semantic_eq(skip)]
120 pub close: T![')'],
121}
122
123#[node]
125#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
126#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
127#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
128#[derive(csskit_derives::NodeWithMetadata)]
129pub struct GrayscaleFunction<'a> {
130 #[atom(CssAtomSet::Grayscale)]
131 pub name: T![Function],
132 pub value: Option<NumericValue<'a, NumberOrPercentage>>,
133 #[semantic_eq(skip)]
134 pub close: T![')'],
135}
136
137#[node]
139#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
140#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
141#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
142#[derive(csskit_derives::NodeWithMetadata)]
143pub struct HueRotateFunction<'a> {
144 #[atom(CssAtomSet::HueRotate)]
145 pub name: T![Function],
146 pub angle: Option<CalcableValue<'a, AngleOrZero>>,
147 #[semantic_eq(skip)]
148 pub close: T![')'],
149}
150
151#[node]
153#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
154#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
155#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
156#[derive(csskit_derives::NodeWithMetadata)]
157pub struct InvertFunction<'a> {
158 #[atom(CssAtomSet::Invert)]
159 pub name: T![Function],
160 pub value: Option<NumericValue<'a, NumberOrPercentage>>,
161 #[semantic_eq(skip)]
162 pub close: T![')'],
163}
164
165#[node]
167#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
168#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
169#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
170#[derive(csskit_derives::NodeWithMetadata)]
171pub struct OpacityFunction<'a> {
172 #[atom(CssAtomSet::Opacity)]
173 pub name: T![Function],
174 pub value: Option<NumericValue<'a, NumberOrPercentage>>,
175 #[semantic_eq(skip)]
176 pub close: T![')'],
177}
178
179#[node]
181#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
182#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
183#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
184#[derive(csskit_derives::NodeWithMetadata)]
185pub struct SaturateFunction<'a> {
186 #[atom(CssAtomSet::Saturate)]
187 pub name: T![Function],
188 pub value: Option<NumericValue<'a, NumberOrPercentage>>,
189 #[semantic_eq(skip)]
190 pub close: T![')'],
191}
192
193#[node]
195#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
196#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
197#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
198#[derive(csskit_derives::NodeWithMetadata)]
199pub struct SepiaFunction<'a> {
200 #[atom(CssAtomSet::Sepia)]
201 pub name: T![Function],
202 pub value: Option<NumericValue<'a, NumberOrPercentage>>,
203 #[semantic_eq(skip)]
204 pub close: T![')'],
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210 use crate::CssAtomSet;
211 use css_parse::{assert_parse, assert_parse_error, assert_peek_false};
212
213 #[test]
214 fn test_filter_function_parses() {
215 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "blur()");
216 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "blur(5px)");
217 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "brightness(0.5)");
218 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "brightness(50%)");
219 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "contrast(2)");
220 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "contrast(200%)");
221 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "drop-shadow(2px 4px)");
222 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "drop-shadow(2px 4px 3px)");
223 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "drop-shadow(red 2px 4px)");
224 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "drop-shadow(red 2px 4px 5px)");
225 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "drop-shadow(calc(2px) var(--y))");
226 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "grayscale(1)");
227 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "grayscale(100%)");
228 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "hue-rotate(90deg)");
229 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "hue-rotate(0)");
230 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "invert(0.5)");
231 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "invert(50%)");
232 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "opacity(0.5)");
233 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "opacity(50%)");
234 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "saturate(2)");
235 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "saturate(200%)");
236 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "sepia(0.5)");
237 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "sepia(50%)");
238 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "blur(calc(5px))");
240 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "brightness(var(--b))");
241 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "hue-rotate(calc(90deg))");
242 assert_parse!(CssAtomSet::ATOMS, FilterFunction, "opacity(var(--o))");
243 }
244
245 #[test]
246 fn test_filter_function_errors() {
247 assert_peek_false!(CssAtomSet::ATOMS, FilterFunction, "none");
248 assert_peek_false!(CssAtomSet::ATOMS, FilterFunction, "foo()");
249 assert_parse_error!(CssAtomSet::ATOMS, FilterFunction, "blur(-5px)");
250 }
251
252 #[test]
253 fn test_filter_value_list_parses() {
254 assert_parse!(CssAtomSet::ATOMS, FilterValueList, "blur(5px)");
255 assert_parse!(CssAtomSet::ATOMS, FilterValueList, "blur(5px)brightness(0.5)");
256 assert_parse!(CssAtomSet::ATOMS, FilterValueList, "blur(5px)contrast(200%)grayscale(0.5)");
257 assert_parse!(CssAtomSet::ATOMS, FilterValueList, "url(\"filter.svg\")");
258 }
259
260 #[test]
261 fn test_filter_value_list_errors() {
262 assert_peek_false!(CssAtomSet::ATOMS, FilterValueList, "none");
263 }
264}