Skip to main content

css_ast/functions/
palette_mix_function.rs

1use super::prelude::*;
2use crate::{CalcableValue, ColorInterpolationMethod, PaletteIdentifier, Percentage, Ranged, Value};
3
4/// <https://drafts.csswg.org/css-fonts-5/#typedef-palette-mix-function>
5///
6/// ```text,ignore
7/// <palette-mix()> = palette-mix( <color-interpolation-method> , [ [ normal | light | dark | <palette-identifier> ] && <percentage [0%,100%]>? ]#{2} )
8/// ```
9#[node]
10#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
12#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(all))]
13#[derive(csskit_derives::NodeWithMetadata)]
14pub struct PaletteMixFunction<'a> {
15	#[atom(CssAtomSet::PaletteMix)]
16	#[cfg_attr(feature = "visitable", visit(skip))]
17	pub name: T![Function],
18	pub interpolation: ColorInterpolationMethod,
19	#[cfg_attr(feature = "visitable", visit(skip))]
20	#[semantic_eq(skip)]
21	pub interpolation_comma: Option<T![,]>,
22	pub first: PaletteMixFunctionParams<'a>,
23	#[cfg_attr(feature = "visitable", visit(skip))]
24	#[semantic_eq(skip)]
25	pub comma: Option<T![,]>,
26	pub second: PaletteMixFunctionParams<'a>,
27	#[cfg_attr(feature = "visitable", visit(skip))]
28	#[semantic_eq(skip)]
29	pub close: T![')'],
30}
31
32/// A palette with an optional percentage in a `palette-mix()` function.
33///
34/// ```text,ignore
35/// [ normal | light | dark | <palette-identifier> ] && <percentage [0%,100%]>?
36/// ```
37///
38/// The palette and percentage can appear in either order.
39#[node]
40#[derive(ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
42#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
43#[derive(csskit_derives::NodeWithMetadata)]
44pub struct PaletteMixFunctionParams<'a> {
45	pub palette: PaletteMixPalette<'a>,
46	pub percentage: Option<CalcableValue<'a, Ranged<Percentage, 0, 100>>>,
47}
48
49impl<'a> Peek<'a> for PaletteMixFunctionParams<'a> {
50	const PEEK_KINDSET: KindSet =
51		PaletteMixPalette::PEEK_KINDSET.combine(CalcableValue::<Ranged<Percentage, 0, 100>>::PEEK_KINDSET);
52
53	#[inline(always)]
54	fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
55	where
56		I: Iterator<Item = Cursor> + Clone,
57	{
58		PaletteMixPalette::peek(p, c) || CalcableValue::<Ranged<Percentage, 0, 100>>::peek(p, c)
59	}
60}
61
62impl<'a> Parse<'a> for PaletteMixFunctionParams<'a> {
63	fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
64	where
65		I: Iterator<Item = Cursor> + Clone,
66	{
67		let mut palette = p.parse_if_peek::<PaletteMixPalette>()?;
68		let percentage = p.parse_if_peek::<CalcableValue<Ranged<Percentage, 0, 100>>>()?;
69		if palette.is_none() {
70			palette = Some(p.parse::<PaletteMixPalette>()?);
71		}
72		Ok(Self { palette: palette.unwrap(), percentage })
73	}
74}
75
76/// The palette selected by one side of a `palette-mix()` function.
77///
78/// ```text,ignore
79/// normal | light | dark | <palette-identifier>
80/// ```
81#[node]
82#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
83#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
84#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
85#[derive(csskit_derives::NodeWithMetadata)]
86pub enum PaletteMixPalette<'a> {
87	#[atom(CssAtomSet::Normal)]
88	Normal(T![Ident]),
89	#[atom(CssAtomSet::Light)]
90	Light(T![Ident]),
91	#[atom(CssAtomSet::Dark)]
92	Dark(T![Ident]),
93	Identifier(Value<'a, PaletteIdentifier>),
94}
95
96#[cfg(test)]
97mod tests {
98	use super::*;
99	use crate::CssAtomSet;
100	use css_parse::{assert_parse, assert_parse_error};
101
102	#[test]
103	fn test_writes() {
104		assert_parse!(CssAtomSet::ATOMS, PaletteMixFunction, "palette-mix(in lch,normal,dark)");
105		assert_parse!(CssAtomSet::ATOMS, PaletteMixFunction, "palette-mix(in oklab,light 40%,dark 60%)");
106		assert_parse!(CssAtomSet::ATOMS, PaletteMixFunction, "palette-mix(in lch,40% light,60% dark)");
107		assert_parse!(CssAtomSet::ATOMS, PaletteMixFunction, "palette-mix(in lch longer hue,--blues 30%,--reds)");
108		assert_parse!(CssAtomSet::ATOMS, PaletteMixFunction, "palette-mix(in lch,--blues var(--pct),--reds)");
109	}
110
111	#[test]
112	fn test_errors() {
113		assert_parse_error!(CssAtomSet::ATOMS, PaletteMixFunction, "palette-mix(normal,dark)");
114		assert_parse_error!(CssAtomSet::ATOMS, PaletteMixFunction, "palette-mix(in lch,normal)");
115		assert_parse_error!(CssAtomSet::ATOMS, PaletteMixFunction, "palette-mix(in lch,normal,dark,light)");
116		assert_parse_error!(CssAtomSet::ATOMS, PaletteMixFunction, "palette-mix(in lch,normal 120%,dark)");
117		assert_parse_error!(CssAtomSet::ATOMS, PaletteMixFunction, "palette-mix(in lch,30%,dark)");
118	}
119
120	#[test]
121	#[cfg(feature = "visitable")]
122	fn test_visits() {
123		use crate::assert_visits;
124		assert_visits!(
125			"palette-mix(in lch,--blues 30%,--reds)",
126			PaletteMixFunction,
127			ColorInterpolationMethod,
128			PaletteMixPalette,
129			Percentage,
130			PaletteMixPalette,
131		);
132	}
133}