1use std::fmt::Write;
8
9use super::{
10 parsing::{rcs_enabled, ChannelKeyword},
11 AbsoluteColor,
12};
13use crate::derives::*;
14use crate::{
15 parser::ParserContext,
16 values::{
17 animated::ToAnimatedValue,
18 computed,
19 generics::calc::CalcUnits,
20 specified::calc::{AllowParse, CalcNode, Leaf},
21 specified::NoCalcNumber,
22 },
23};
24use cssparser::{color::OPAQUE, Parser, Token};
25use style_traits::{ParseError, ToCss, StyleParseErrorKind};
26
27#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
29#[repr(u8)]
30pub enum ColorComponent<ValueType> {
31 None,
33 Value(ValueType),
35 ChannelKeyword(ChannelKeyword),
37 Calc(Box<CalcNode>),
39 AlphaOmitted,
41}
42
43impl<ValueType> ColorComponent<ValueType> {
44 #[inline]
46 pub fn is_none(&self) -> bool {
47 matches!(self, Self::None)
48 }
49}
50
51pub trait ColorComponentType: Sized + Clone {
54 fn from_value(value: f32) -> Self;
59
60 fn units() -> CalcUnits;
62
63 fn try_from_token(token: &Token) -> Result<Self, ()>;
65
66 fn try_from_leaf(leaf: &Leaf) -> Result<Self, ()>;
69}
70
71impl<ValueType: ColorComponentType> ColorComponent<ValueType> {
72 pub fn parse<'i, 't>(
74 context: &ParserContext,
75 input: &mut Parser<'i, 't>,
76 allow_none: bool,
77 allow_channel_keyword: bool,
78 ) -> Result<Self, ParseError<'i>> {
79 let location = input.current_source_location();
80
81 match *input.next()? {
82 Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => {
83 Ok(ColorComponent::None)
84 },
85 ref t @ Token::Ident(ref ident) if allow_channel_keyword => {
86 let Ok(channel_keyword) = ChannelKeyword::from_ident(ident) else {
87 return Err(location.new_unexpected_token_error(t.clone()));
88 };
89 Ok(ColorComponent::ChannelKeyword(channel_keyword))
90 },
91 Token::Function(ref name) => {
92 let function = CalcNode::math_function(context, name, location)?;
93 let mut allow = AllowParse::new(ValueType::units());
94 allow.color_components = rcs_enabled() && allow_channel_keyword;
95 let mut node = CalcNode::parse(context, input, function, allow)?;
96 node.simplify_and_sort();
97 if node.unit().is_err() {
98 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
99 }
100 Ok(Self::Calc(Box::new(node)))
101 },
102 ref t => ValueType::try_from_token(t)
103 .map(Self::Value)
104 .map_err(|_| location.new_unexpected_token_error(t.clone())),
105 }
106 }
107
108 pub fn resolve(
110 &self,
111 origin_color: Option<&AbsoluteColor>,
112 context: Option<&computed::Context>,
113 ) -> Result<Option<ValueType>, ()> {
114 Ok(match self {
115 ColorComponent::None => None,
116 ColorComponent::Value(value) => Some(value.clone()),
117 ColorComponent::ChannelKeyword(channel_keyword) => match origin_color {
118 Some(origin_color) => {
119 let value = origin_color.get_component_by_channel_keyword(*channel_keyword)?;
120 Some(ValueType::from_value(value.unwrap_or(0.0)))
121 },
122 None => return Err(()),
123 },
124 ColorComponent::Calc(node) => {
125 let resolved_leaf = node.resolve_computed(context, |leaf| match leaf {
126 Leaf::ColorComponent(channel_keyword) => match origin_color {
129 Some(origin_color) => origin_color
130 .get_component_by_channel_keyword(*channel_keyword)
131 .map(|v| Leaf::Number(NoCalcNumber::new(v.unwrap_or(0.0)))),
132 None => Err(()),
133 },
134 _ => Ok(leaf.clone()),
135 })?;
136
137 Some(ValueType::try_from_leaf(&resolved_leaf)?)
138 },
139 ColorComponent::AlphaOmitted => {
140 if let Some(origin_color) = origin_color {
141 origin_color.alpha().map(ValueType::from_value)
146 } else {
147 Some(ValueType::from_value(OPAQUE))
148 }
149 },
150 })
151 }
152}
153
154impl<ValueType: ToCss> ToCss for ColorComponent<ValueType> {
155 fn to_css<W>(&self, dest: &mut style_traits::CssWriter<W>) -> std::fmt::Result
156 where
157 W: Write,
158 {
159 match self {
160 ColorComponent::None => dest.write_str("none")?,
161 ColorComponent::Value(value) => value.to_css(dest)?,
162 ColorComponent::ChannelKeyword(channel_keyword) => channel_keyword.to_css(dest)?,
163 ColorComponent::Calc(node) => {
164 node.to_css(dest)?;
169 },
170 ColorComponent::AlphaOmitted => {
171 debug_assert!(false, "can't serialize an omitted alpha component");
172 },
173 }
174
175 Ok(())
176 }
177}
178
179impl<ValueType> ToAnimatedValue for ColorComponent<ValueType> {
180 type AnimatedValue = Self;
181
182 fn to_animated_value(self, _context: &crate::values::animated::Context) -> Self::AnimatedValue {
183 self
184 }
185
186 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
187 animated
188 }
189}