css_ast/functions/
color_function.rs

1use crate::{AngleOrNumber, NoneOr, NumberOrPercentage};
2use css_parse::{Build, Cursor, Function, Parser, Peek, T, function_set, keyword_set};
3use csskit_derives::{Parse, Peek, ToCursors, ToSpan, Visitable};
4
5keyword_set!(pub enum ColorSpace {
6	Srgb: "srgb",
7	SrgbLinear: "srgb-linear",
8	DisplayP3: "display-p3",
9	A98Rgb: "a98-rgb",
10	ProphotoRgb: "prophoto-rgb",
11	Rec2020: "rec2020",
12	Xyz: "xyz",
13	XyzD50: "xyz-d50",
14	XyzD65: "xyz-d65",
15});
16
17function_set!(pub struct ColorFunctionName "color");
18function_set!(pub struct RgbFunctionName "rgb");
19function_set!(pub struct RgbaFunctionName "rgba");
20function_set!(pub struct HslFunctionName "hsl");
21function_set!(pub struct HslaFunctionName "hsla");
22function_set!(pub struct HwbFunctionName "hwb");
23function_set!(pub struct LabFunctionName "lab");
24function_set!(pub struct LchFunctionName "lch");
25function_set!(pub struct OklabFunctionName "oklab");
26function_set!(pub struct OklchFunctionName "oklch");
27
28#[derive(ToCursors, ToSpan, Visitable, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
30#[visit(self)]
31pub struct CommaOrSlash(T![Delim]);
32
33impl<'a> Peek<'a> for CommaOrSlash {
34	fn peek(_: &Parser<'a>, c: Cursor) -> bool {
35		c == ',' || c == '/'
36	}
37}
38
39impl<'a> Build<'a> for CommaOrSlash {
40	fn build(p: &Parser<'a>, c: Cursor) -> Self {
41		debug_assert!(Self::peek(p, c));
42		Self(<T![Delim]>::build(p, c))
43	}
44}
45
46/// <https://drafts.csswg.org/css-color/#typedef-color-function>
47#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
49#[visit(self)]
50pub enum ColorFunction {
51	Color(ColorFunctionColor),
52	Rgb(RgbFunction),
53	Rgba(RgbaFunction),
54	Hsl(HslFunction),
55	Hsla(HslaFunction),
56	Hwb(HwbFunction),
57	Lab(LabFunction),
58	Lch(LchFunction),
59	Oklab(OklabFunction),
60	Oklch(OklchFunction),
61}
62
63#[cfg(feature = "chromashift")]
64impl crate::ToChromashift for ColorFunction {
65	fn to_chromashift(&self) -> Option<chromashift::Color> {
66		match self {
67			Self::Color(c) => c.to_chromashift(),
68			Self::Rgb(c) => c.to_chromashift(),
69			Self::Rgba(c) => c.to_chromashift(),
70			Self::Hsl(c) => c.to_chromashift(),
71			Self::Hsla(c) => c.to_chromashift(),
72			Self::Hwb(c) => c.to_chromashift(),
73			Self::Lab(c) => c.to_chromashift(),
74			Self::Lch(c) => c.to_chromashift(),
75			Self::Oklab(c) => c.to_chromashift(),
76			Self::Oklch(c) => c.to_chromashift(),
77		}
78	}
79}
80
81/// <https://drafts.csswg.org/css-color/#funcdef-color>
82///
83/// ```text,ignore
84/// color() = color( <colorspace-params> [ / [ <alpha-value> | none ] ]? )
85/// <colorspace-params> = [ <predefined-rgb-params> | <xyz-params>]
86/// <predefined-rgb-params> = <predefined-rgb> [ <number> | <percentage> | none ]{3}
87/// <predefined-rgb> = srgb | srgb-linear | display-p3 | a98-rgb | prophoto-rgb | rec2020
88/// <xyz-params> = <xyz-space> [ <number> | <percentage> | none ]{3}
89/// <xyz-space> = xyz | xyz-d50 | xyz-d65
90/// ```
91#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
92#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
93#[visit(self)]
94pub struct ColorFunctionColor(Function<ColorFunctionName, ColorFunctionColorParams>);
95
96#[cfg(feature = "chromashift")]
97impl crate::ToChromashift for ColorFunctionColor {
98	fn to_chromashift(&self) -> Option<chromashift::Color> {
99		todo!();
100	}
101}
102
103#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
104#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
105#[visit(self)]
106pub struct ColorFunctionColorParams(
107	pub ColorSpace,
108	pub NoneOr<NumberOrPercentage>,
109	pub NoneOr<NumberOrPercentage>,
110	pub NoneOr<NumberOrPercentage>,
111	pub Option<T![/]>,
112	pub Option<NoneOr<NumberOrPercentage>>,
113);
114
115/// <https://drafts.csswg.org/css-color/#funcdef-rgb>
116///
117/// ```text,ignore
118/// rgb() = [ <legacy-rgb-syntax> | <modern-rgb-syntax> ]
119/// rgba() = [ <legacy-rgba-syntax> | <modern-rgba-syntax> ]
120/// <legacy-rgb-syntax> =   rgb( <percentage>#{3} , <alpha-value>? ) |
121///                   rgb( <number>#{3} , <alpha-value>? )
122/// <legacy-rgba-syntax> = rgba( <percentage>#{3} , <alpha-value>? ) |
123///                   rgba( <number>#{3} , <alpha-value>? )
124/// <modern-rgb-syntax> = rgb(
125///   [ <number> | <percentage> | none]{3}
126///   [ / [<alpha-value> | none] ]?  )
127/// <modern-rgba-syntax> = rgba(
128///   [ <number> | <percentage> | none]{3}
129///   [ / [<alpha-value> | none] ]?  )
130/// ```
131#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
132#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
133#[visit(self)]
134pub struct RgbFunction(Function<RgbFunctionName, RgbFunctionParams>);
135
136#[cfg(feature = "chromashift")]
137impl crate::ToChromashift for RgbFunction {
138	fn to_chromashift(&self) -> Option<chromashift::Color> {
139		self.0.parameters.to_chromashift()
140	}
141}
142
143#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
144#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
145#[visit(self)]
146pub struct RgbaFunction(Function<RgbaFunctionName, RgbFunctionParams>);
147
148#[cfg(feature = "chromashift")]
149impl crate::ToChromashift for RgbaFunction {
150	fn to_chromashift(&self) -> Option<chromashift::Color> {
151		self.0.parameters.to_chromashift()
152	}
153}
154
155#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
156#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
157#[visit(self)]
158pub struct RgbFunctionParams(
159	pub NoneOr<NumberOrPercentage>,
160	pub Option<T![,]>,
161	pub NoneOr<NumberOrPercentage>,
162	pub Option<T![,]>,
163	pub NoneOr<NumberOrPercentage>,
164	pub Option<CommaOrSlash>,
165	pub Option<NoneOr<NumberOrPercentage>>,
166);
167
168#[cfg(feature = "chromashift")]
169impl crate::ToChromashift for RgbFunctionParams {
170	fn to_chromashift(&self) -> Option<chromashift::Color> {
171		use chromashift::Srgb;
172		let Self(red, _, green, _, blue, _, alpha) = &self;
173		let alpha = match alpha {
174			Some(NoneOr::None(_)) => 0.0,
175			Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
176			Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
177			None => 100.0,
178		};
179		let red = match red {
180			NoneOr::None(_) => {
181				return None;
182			}
183			NoneOr::Some(NumberOrPercentage::Number(red)) => red.value(),
184			NoneOr::Some(NumberOrPercentage::Percentage(red)) => red.value() / 100.0 * 255.0,
185		} as u8;
186		let green = match green {
187			NoneOr::None(_) => {
188				return None;
189			}
190			NoneOr::Some(NumberOrPercentage::Number(green)) => green.value(),
191			NoneOr::Some(NumberOrPercentage::Percentage(green)) => green.value() / 100.0 * 255.0,
192		} as u8;
193		let blue = match blue {
194			NoneOr::None(_) => {
195				return None;
196			}
197			NoneOr::Some(NumberOrPercentage::Number(blue)) => blue.value(),
198			NoneOr::Some(NumberOrPercentage::Percentage(blue)) => blue.value() / 100.0 * 255.0,
199		} as u8;
200		Some(chromashift::Color::Srgb(Srgb::new(red, green, blue, alpha)))
201	}
202}
203
204/// <https://drafts.csswg.org/css-color/#funcdef-hsl>
205///
206/// ```text,ignore
207/// hsl() = [ <legacy-hsl-syntax> | <modern-hsl-syntax> ]
208/// hsla() = [ <legacy-hsla-syntax> | <modern-hsla-syntax> ]
209/// <modern-hsl-syntax> = hsl(
210///     [<hue> | none]
211///     [<percentage> | <number> | none]
212///     [<percentage> | <number> | none]
213///     [ / [<alpha-value> | none] ]? )
214/// <modern-hsla-syntax> = hsla(
215///     [<hue> | none]
216///     [<percentage> | <number> | none]
217///     [<percentage> | <number> | none]
218///     [ / [<alpha-value> | none] ]? )
219/// <legacy-hsl-syntax> = hsl( <hue>, <percentage>, <percentage>, <alpha-value>? )
220/// <legacy-hsla-syntax> = hsla( <hue>, <percentage>, <percentage>, <alpha-value>? )
221/// ```
222#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
223#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
224#[visit(self)]
225pub struct HslFunction(Function<HslFunctionName, HslFunctionParams>);
226
227#[cfg(feature = "chromashift")]
228impl crate::ToChromashift for HslFunction {
229	fn to_chromashift(&self) -> Option<chromashift::Color> {
230		self.0.parameters.to_chromashift()
231	}
232}
233
234#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
235#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
236#[visit(self)]
237pub struct HslaFunction(Function<HslaFunctionName, HslFunctionParams>);
238
239#[cfg(feature = "chromashift")]
240impl crate::ToChromashift for HslaFunction {
241	fn to_chromashift(&self) -> Option<chromashift::Color> {
242		self.0.parameters.to_chromashift()
243	}
244}
245
246#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
247#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
248#[visit(self)]
249pub struct HslFunctionParams(
250	pub NoneOr<AngleOrNumber>,
251	pub Option<T![,]>,
252	pub NoneOr<NumberOrPercentage>,
253	pub Option<T![,]>,
254	pub NoneOr<NumberOrPercentage>,
255	pub Option<CommaOrSlash>,
256	pub Option<NoneOr<NumberOrPercentage>>,
257);
258
259#[cfg(feature = "chromashift")]
260impl crate::ToChromashift for HslFunctionParams {
261	fn to_chromashift(&self) -> Option<chromashift::Color> {
262		use chromashift::Hsl;
263		let Self(hue, _, saturation, _, lightness, _, alpha) = &self;
264		let hue = match hue {
265			NoneOr::None(_) => {
266				return None;
267			}
268			NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
269			NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
270		};
271		let saturation = match saturation {
272			NoneOr::None(_) => {
273				return None;
274			}
275			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
276			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
277		};
278		let lightness = match lightness {
279			NoneOr::None(_) => {
280				return None;
281			}
282			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
283			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
284		};
285		let alpha = match alpha {
286			Some(NoneOr::None(_)) => 0.0,
287			Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
288			Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
289			None => 100.0,
290		};
291		Some(chromashift::Color::Hsl(Hsl::new(hue, saturation, lightness, alpha)))
292	}
293}
294
295// https://drafts.csswg.org/css-color/#funcdef-hwb
296// hwb() = hwb(
297//  [<hue> | none]
298//  [<percentage> | <number> | none]
299//  [<percentage> | <number> | none]
300//  [ / [<alpha-value> | none] ]? )
301#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
302#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
303#[visit(self)]
304pub struct HwbFunction(Function<HwbFunctionName, HwbFunctionParams>);
305
306#[cfg(feature = "chromashift")]
307impl crate::ToChromashift for HwbFunction {
308	fn to_chromashift(&self) -> Option<chromashift::Color> {
309		use chromashift::Hwb;
310		let HwbFunctionParams(hue, whiteness, blackness, _, alpha) = &self.0.parameters;
311		let hue = match hue {
312			NoneOr::None(_) => {
313				return None;
314			}
315			NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
316			NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
317		};
318		let whiteness = match whiteness {
319			NoneOr::None(_) => {
320				return None;
321			}
322			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
323			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
324		};
325		let blackness = match blackness {
326			NoneOr::None(_) => {
327				return None;
328			}
329			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
330			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
331		};
332		let alpha = match alpha {
333			Some(NoneOr::None(_)) => 0.0,
334			Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
335			Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
336			None => 100.0,
337		};
338		Some(chromashift::Color::Hwb(Hwb::new(hue, whiteness, blackness, alpha)))
339	}
340}
341
342#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
343#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
344#[visit(self)]
345pub struct HwbFunctionParams(
346	pub NoneOr<AngleOrNumber>,
347	pub NoneOr<NumberOrPercentage>,
348	pub NoneOr<NumberOrPercentage>,
349	pub Option<T![/]>,
350	pub Option<NoneOr<NumberOrPercentage>>,
351);
352
353/// <https://drafts.csswg.org/css-color/#funcdef-lab>
354///
355/// ```text,ignore
356/// lab() = lab( [<percentage> | <number> | none]
357///  [ <percentage> | <number> | none]
358///  [ <percentage> | <number> | none]
359///  [ / [<alpha-value> | none] ]? )
360/// ```
361#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
362#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
363#[visit(self)]
364pub struct LabFunction(Function<LabFunctionName, LabFunctionParams>);
365
366#[cfg(feature = "chromashift")]
367impl crate::ToChromashift for LabFunction {
368	fn to_chromashift(&self) -> Option<chromashift::Color> {
369		use chromashift::Lab;
370		let LabFunctionParams(l, a, b, _, alpha) = &self.0.parameters;
371		let l = match l {
372			NoneOr::None(_) => {
373				return None;
374			}
375			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
376			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
377		} as f64;
378		let a = match a {
379			NoneOr::None(_) => {
380				return None;
381			}
382			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
383			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 125.0,
384		} as f64;
385		let b = match b {
386			NoneOr::None(_) => {
387				return None;
388			}
389			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
390			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 125.0,
391		} as f64;
392		let alpha = match alpha {
393			Some(NoneOr::None(_)) => 0.0,
394			Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
395			Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
396			None => 100.0,
397		};
398		Some(chromashift::Color::Lab(Lab::new(l, a, b, alpha)))
399	}
400}
401
402#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
403#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
404#[visit(self)]
405pub struct LabFunctionParams(
406	pub NoneOr<NumberOrPercentage>,
407	pub NoneOr<NumberOrPercentage>,
408	pub NoneOr<NumberOrPercentage>,
409	pub Option<T![/]>,
410	pub Option<NoneOr<NumberOrPercentage>>,
411);
412
413/// <https://drafts.csswg.org/css-color/#funcdef-lch>
414///
415/// ```text,ignore
416/// lch() = lch( [<percentage> | <number> | none]
417///  [ <percentage> | <number> | none]
418///  [ <hue> | none]
419///  [ / [<alpha-value> | none] ]? )
420/// ```
421#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
422#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
423#[visit(self)]
424pub struct LchFunction(Function<LchFunctionName, LchFunctionParams>);
425
426#[cfg(feature = "chromashift")]
427impl crate::ToChromashift for LchFunction {
428	fn to_chromashift(&self) -> Option<chromashift::Color> {
429		use chromashift::Lch;
430		let LchFunctionParams(lightness, chroma, hue, _, alpha) = &self.0.parameters;
431		let lightness = match lightness {
432			NoneOr::None(_) => {
433				return None;
434			}
435			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
436			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
437		} as f64;
438		let chroma = match chroma {
439			NoneOr::None(_) => {
440				return None;
441			}
442			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
443			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 150.0,
444		} as f64;
445		let hue = match hue {
446			NoneOr::None(_) => {
447				return None;
448			}
449			NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
450			NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
451		} as f64;
452		let alpha = match alpha {
453			Some(NoneOr::None(_)) => 0.0,
454			Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
455			Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
456			None => 100.0,
457		};
458		Some(chromashift::Color::Lch(Lch::new(lightness, chroma, hue, alpha)))
459	}
460}
461
462#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
463#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
464#[visit(self)]
465pub struct LchFunctionParams(
466	pub NoneOr<NumberOrPercentage>,
467	pub NoneOr<NumberOrPercentage>,
468	pub NoneOr<AngleOrNumber>,
469	pub Option<T![/]>,
470	pub Option<NoneOr<NumberOrPercentage>>,
471);
472
473/// <https://drafts.csswg.org/css-color/#funcdef-oklab>
474///
475/// ```text,ignore
476/// oklab() = oklab( [ <percentage> | <number> | none]
477///  [ <percentage> | <number> | none]
478///  [ <percentage> | <number> | none]
479///  [ / [<alpha-value> | none] ]? )
480///  ```
481#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
482#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
483#[visit(self)]
484pub struct OklabFunction(Function<OklabFunctionName, LabFunctionParams>);
485
486#[cfg(feature = "chromashift")]
487impl crate::ToChromashift for OklabFunction {
488	fn to_chromashift(&self) -> Option<chromashift::Color> {
489		use chromashift::Oklab;
490		let LabFunctionParams(l, a, b, _, alpha) = &self.0.parameters;
491		let alpha = match alpha {
492			Some(NoneOr::None(_)) => 0.0,
493			Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
494			Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
495			None => 100.0,
496		};
497		let l = match l {
498			NoneOr::None(_) => {
499				return None;
500			}
501			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
502			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0,
503		} as f64;
504		let a = match a {
505			NoneOr::None(_) => {
506				return None;
507			}
508			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
509			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 0.4,
510		} as f64;
511		let b = match b {
512			NoneOr::None(_) => {
513				return None;
514			}
515			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
516			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 0.4,
517		} as f64;
518		Some(chromashift::Color::Oklab(Oklab::new(l, a, b, alpha)))
519	}
520}
521
522/// <https://drafts.csswg.org/css-color/#funcdef-oklch>
523///
524/// ```text,ignore
525/// oklab() = oklab( [ <percentage> | <number> | none]
526///  [ <percentage> | <number> | none]
527///  [ <percentage> | <number> | none]
528///  [ / [<alpha-value> | none] ]? )
529///  ```
530#[derive(Parse, Peek, ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
531#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
532#[visit(self)]
533pub struct OklchFunction(Function<OklchFunctionName, LchFunctionParams>);
534
535#[cfg(feature = "chromashift")]
536impl crate::ToChromashift for OklchFunction {
537	fn to_chromashift(&self) -> Option<chromashift::Color> {
538		use chromashift::Oklch;
539		let LchFunctionParams(lightness, chroma, hue, _, alpha) = &self.0.parameters;
540		let lightness = match lightness {
541			NoneOr::None(_) => {
542				return None;
543			}
544			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
545			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
546		} as f64;
547		let chroma = match chroma {
548			NoneOr::None(_) => {
549				return None;
550			}
551			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
552			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 150.0,
553		} as f64;
554		let hue = match hue {
555			NoneOr::None(_) => {
556				return None;
557			}
558			NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
559			NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
560		} as f64;
561		let alpha = match alpha {
562			Some(NoneOr::None(_)) => 0.0,
563			Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
564			Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
565			None => 100.0,
566		};
567		Some(chromashift::Color::Oklch(Oklch::new(lightness, chroma, hue, alpha)))
568	}
569}
570
571#[cfg(test)]
572mod tests {
573	use super::*;
574
575	#[test]
576	fn size_test() {
577		assert_eq!(std::mem::size_of::<ColorFunction>(), 144);
578		assert_eq!(std::mem::size_of::<ColorFunctionColor>(), 124);
579		assert_eq!(std::mem::size_of::<RgbFunction>(), 140);
580		assert_eq!(std::mem::size_of::<RgbaFunction>(), 140);
581		assert_eq!(std::mem::size_of::<HslFunction>(), 140);
582		assert_eq!(std::mem::size_of::<HslaFunction>(), 140);
583		assert_eq!(std::mem::size_of::<HwbFunction>(), 108);
584		assert_eq!(std::mem::size_of::<LabFunction>(), 108);
585		assert_eq!(std::mem::size_of::<LchFunction>(), 108);
586		assert_eq!(std::mem::size_of::<OklabFunction>(), 108);
587		assert_eq!(std::mem::size_of::<OklchFunction>(), 108);
588	}
589}