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