Skip to main content

css_ast/rules/
color_profile.rs

1use super::prelude::*;
2use crate::Url;
3#[cfg(feature = "visitable")]
4use crate::visit::{NodeId, QueryableNode};
5
6/// Represents the `@color-profile` at-rule, e.g. `@color-profile --swop5c { src: url(swop.icc) }`.
7///
8/// ```md
9/// <color-profile-rule>
10///  │├─ "@color-profile" ─╮─ <dashed-ident> ─╭─ <block> ─┤│
11///                        ╰─ "device-cmyk" ──╯
12/// ```
13///
14/// <https://drafts.csswg.org/css-color-5/#at-profile>
15#[node]
16#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
18#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit, queryable(skip))]
19#[cfg_attr(
20	feature = "css_feature_data",
21	derive(::csskit_derives::ToCSSFeature),
22	css_feature("css.at-rules.color-profile")
23)]
24#[derive(csskit_derives::NodeWithMetadata)]
25#[metadata(node_kinds = AtRule, used_at_rules = ColorProfile, property_kinds = Name)]
26pub struct ColorProfileRule<'a> {
27	#[cfg_attr(feature = "visitable", visit(skip))]
28	#[atom(CssAtomSet::ColorProfile)]
29	pub name: T![AtKeyword],
30	pub prelude: ColorProfilePrelude,
31	#[metadata(block)]
32	pub block: ColorProfileRuleBlock<'a>,
33}
34
35#[cfg(feature = "visitable")]
36impl<'a> QueryableNode for ColorProfileRule<'a> {
37	const NODE_ID: NodeId = NodeId::ColorProfileRule;
38
39	fn get_property(&self, kind: PropertyKind) -> Option<Cursor> {
40		match kind {
41			PropertyKind::Name => Some(self.prelude.ident()),
42			_ => None,
43		}
44	}
45}
46
47#[node]
48#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
50#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
51#[derive(csskit_derives::NodeWithMetadata)]
52pub enum ColorProfilePrelude {
53	Name(T![DashedIdent]),
54	#[atom(CssAtomSet::DeviceCmyk)]
55	DeviceCmyk(T![Ident]),
56}
57
58impl ColorProfilePrelude {
59	/// Returns a cursor to the profile name, e.g. `--swop5c` or `device-cmyk`.
60	pub fn ident(&self) -> Cursor {
61		match self {
62			Self::Name(c) => (*c).into(),
63			Self::DeviceCmyk(c) => (*c).into(),
64		}
65	}
66}
67
68#[node]
69#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
71#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable))]
72#[derive(csskit_derives::NodeWithMetadata)]
73pub struct ColorProfileRuleBlock<'a>(DeclarationList<'a, ColorProfileRuleStyleValue<'a>, CssMetadata>);
74
75#[node]
76#[derive(ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
77#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
78#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
79#[derive(csskit_derives::NodeWithMetadata)]
80pub enum ColorProfileRuleStyleValue<'a> {
81	Src(ColorProfileSrcValue),
82	RenderingIntent(RenderingIntentValue),
83	Components(ComponentsValue<'a>),
84	Unknown(ComponentValues<'a>),
85}
86
87#[node]
88#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
89#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
90#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
91#[derive(csskit_derives::NodeWithMetadata)]
92pub struct ColorProfileSrcValue(Url);
93
94#[node]
95#[derive(
96	Parse, Peek, IntoCursor, ToSpan, SemanticEq, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
97)]
98#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
99#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
100#[derive(csskit_derives::NodeWithMetadata)]
101pub enum RenderingIntentValue {
102	#[atom(CssAtomSet::RelativeColorimetric)]
103	RelativeColorimetric(T![Ident]),
104	#[atom(CssAtomSet::AbsoluteColorimetric)]
105	AbsoluteColorimetric(T![Ident]),
106	#[atom(CssAtomSet::Perceptual)]
107	Perceptual(T![Ident]),
108	#[atom(CssAtomSet::Saturation)]
109	Saturation(T![Ident]),
110}
111
112#[node]
113#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
114#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
115#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
116#[derive(csskit_derives::NodeWithMetadata)]
117pub struct ComponentsValue<'a>(pub CommaSeparated<'a, T![Ident], 1>);
118
119impl<'a> DeclarationValue<'a, CssMetadata> for ColorProfileRuleStyleValue<'a> {
120	fn valid_declaration_name<I>(p: &Parser<'a, I>, c: Cursor) -> bool
121	where
122		I: Iterator<Item = Cursor> + Clone,
123	{
124		matches!(p.to_atom::<CssAtomSet>(c), CssAtomSet::Src | CssAtomSet::RenderingIntent | CssAtomSet::Components)
125	}
126
127	fn is_unknown(&self) -> bool {
128		matches!(self, Self::Unknown(_))
129	}
130
131	fn needs_computing(&self) -> bool {
132		matches!(self, Self::Unknown(_))
133	}
134
135	fn parse_declaration_value<I>(p: &mut Parser<'a, I>, c: Cursor) -> ParserResult<Self>
136	where
137		I: Iterator<Item = Cursor> + Clone,
138	{
139		Ok(match p.to_atom::<CssAtomSet>(c) {
140			CssAtomSet::Src => Self::Src(p.parse::<ColorProfileSrcValue>()?),
141			CssAtomSet::RenderingIntent => Self::RenderingIntent(p.parse::<RenderingIntentValue>()?),
142			CssAtomSet::Components => Self::Components(p.parse::<ComponentsValue<'a>>()?),
143			_ => Self::Unknown(p.parse::<ComponentValues<'a>>()?),
144		})
145	}
146}
147
148#[cfg(test)]
149mod tests {
150	use super::*;
151	use crate::CssAtomSet;
152	use css_parse::assert_parse;
153
154	#[test]
155	fn test_writes() {
156		assert_parse!(CssAtomSet::ATOMS, ColorProfileRule, "@color-profile --swop5c{src:url(\"swop.icc\")}");
157		assert_parse!(CssAtomSet::ATOMS, ColorProfileRule, "@color-profile device-cmyk{src:url(cmyk.icc)}");
158		assert_parse!(
159			CssAtomSet::ATOMS,
160			ColorProfileRule,
161			"@color-profile --unwise{src:url(unwise);components:mi,pi,ni;rendering-intent:perceptual}"
162		);
163	}
164}