1use super::{Angle, Length, Number, Resolution, Time};
8use crate::Zero;
9use crate::derives::*;
10use crate::typed_om::{NumericBaseType, NumericType};
11use crate::values::generics::Optional;
12use crate::values::generics::calc::{self, CalcType, SimplificationResult};
13use debug_unreachable::debug_unreachable;
14use serde::{Deserialize, Serialize};
15
16pub use crate::values::generics::calc::CalcPercentageLeaf;
17
18#[derive(
20 Clone,
21 Debug,
22 Deserialize,
23 MallocSizeOf,
24 PartialEq,
25 Serialize,
26 ToAnimatedZero,
27 ToCss,
28 ToResolvedValue,
29 ToTyped,
30)]
31#[allow(missing_docs)]
32#[repr(u8)]
33pub enum ComputedLeaf {
34 Length(Length),
35 Percentage(CalcPercentageLeaf),
36 Number(Number),
37 Angle(Angle),
38 Time(Time),
39 Resolution(Resolution),
40}
41
42impl ComputedLeaf {
43 pub(super) fn is_zero_length(&self) -> bool {
44 match *self {
45 Self::Length(ref l) => l.is_zero(),
46 Self::Percentage(..)
47 | Self::Number(..)
48 | Self::Angle(..)
49 | Self::Time(..)
50 | Self::Resolution(..) => false,
51 }
52 }
53}
54
55impl calc::CalcNodeLeaf for ComputedLeaf {
56 fn numeric_type(&self) -> NumericType {
57 match self {
58 Self::Length(_) => NumericType::length(),
59 Self::Percentage(p) => p.numeric_type(),
60 Self::Number(_) => NumericType::number(),
61 Self::Angle(_) => NumericType::angle(),
62 Self::Time(_) => NumericType::time(),
63 Self::Resolution(_) => NumericType::resolution(),
64 }
65 }
66
67 fn unitless_value(&self) -> Option<f32> {
68 Some(match *self {
69 Self::Length(ref l) => l.px(),
70 Self::Percentage(ref p) => p.get(),
71 Self::Number(n) => n,
72 Self::Angle(ref a) => a.degrees(),
73 Self::Time(ref t) => t.seconds(),
74 Self::Resolution(ref r) => r.dppx(),
75 })
76 }
77
78 fn canonical_value(&self) -> Option<f32> {
79 Some(match *self {
80 Self::Length(ref l) => l.px(),
81 Self::Percentage(ref p) => match p.hint {
82 Optional::Some(NumericBaseType::Percent) => p.get(),
86 _ => return None,
87 },
88 Self::Number(n) => n,
89 Self::Angle(ref a) => a.degrees(),
90 Self::Time(ref t) => t.seconds(),
91 Self::Resolution(ref r) => r.dppx(),
92 })
93 }
94
95 fn new_number(value: f32) -> Self {
96 Self::Number(value)
97 }
98
99 fn as_number(&self) -> Option<f32> {
100 match *self {
101 Self::Length(_)
102 | Self::Percentage(_)
103 | Self::Angle(_)
104 | Self::Time(_)
105 | Self::Resolution(_) => None,
106 Self::Number(value) => Some(value),
107 }
108 }
109
110 fn as_percentage(&self) -> Option<(f32, Optional<NumericBaseType>)> {
111 match *self {
112 Self::Percentage(p) => Some((p.get(), p.hint)),
113 _ => None,
114 }
115 }
116
117 fn as_angle_radians(&self) -> Option<f32> {
118 match *self {
119 Self::Angle(a) => Some(a.radians()),
120 _ => None,
121 }
122 }
123
124 fn new_angle_from_radians(radians: f32) -> Self {
125 Self::Angle(Angle::from_radians(radians))
126 }
127
128 fn new_from_typed_value(value: f32, numeric_type: NumericType) -> Result<Self, ()> {
129 let calc_type = numeric_type.as_calc_type()?;
130 let percent_hint = numeric_type.percent_hint();
131 Ok(match calc_type {
132 CalcType::Number => Self::new_number(value),
133 CalcType::Length => Self::Length(Length::new(value)),
134 CalcType::Angle => Self::Angle(Angle::from_degrees(value)),
135 CalcType::Time => Self::Time(Time::from_seconds(value)),
136 CalcType::Resolution => Self::Resolution(Resolution::from_dppx(value)),
137 CalcType::Percentage => Self::Percentage(CalcPercentageLeaf::new(value, percent_hint)),
138 })
139 }
140
141 fn compare(&self, other: &Self) -> Option<std::cmp::Ordering> {
142 use self::ComputedLeaf::*;
143 if std::mem::discriminant(self) != std::mem::discriminant(other) {
144 return None;
145 }
146
147 if matches!(self, Percentage(p) if p.hint != Optional::Some(NumericBaseType::Percent)) {
149 return None;
150 }
151
152 let Ok(self_negative) = self.is_negative() else {
153 return None;
154 };
155 let Ok(other_negative) = other.is_negative() else {
156 return None;
157 };
158 if self_negative != other_negative {
159 return Some(if self_negative {
160 std::cmp::Ordering::Less
161 } else {
162 std::cmp::Ordering::Greater
163 });
164 }
165
166 match (self, other) {
167 (Length(one), Length(other)) => one.partial_cmp(other),
168 (Percentage(one), Percentage(other)) => one.value.partial_cmp(&other.value),
169 (Number(one), Number(other)) => one.partial_cmp(other),
170 (Angle(one), Angle(other)) => one.partial_cmp(other),
171 (Time(one), Time(other)) => one.partial_cmp(other),
172 (Resolution(one), Resolution(other)) => one.partial_cmp(other),
173 _ => unsafe {
174 match *self {
175 Length(..) | Percentage(..) | Number(..) | Angle(..) | Time(..)
176 | Resolution(..) => {},
177 }
178 debug_unreachable!("Forgot to handle unit in compare()")
179 },
180 }
181 }
182
183 fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
184 use self::ComputedLeaf::*;
185
186 if self.is_zero_length() {
188 *self = other.clone();
189 return Ok(());
190 }
191
192 if other.is_zero_length() {
193 return Ok(());
194 }
195
196 if std::mem::discriminant(self) != std::mem::discriminant(other) {
197 return Err(());
198 }
199
200 match (self, other) {
201 (&mut Length(ref mut one), Length(other)) => {
202 *one += *other;
203 },
204 (&mut Percentage(ref mut one), Percentage(other)) => {
205 *one = CalcPercentageLeaf::new(one.get() + other.get(), one.combined_hint(other));
206 },
207 (&mut Number(ref mut one), Number(other)) => {
208 *one += *other;
209 },
210 (&mut Angle(ref mut one), Angle(other)) => {
211 *one += *other;
212 },
213 (&mut Time(ref mut one), Time(other)) => {
214 *one += *other;
215 },
216 (&mut Resolution(ref mut one), Resolution(other)) => {
217 *one += *other;
218 },
219 _ => unsafe {
220 match *other {
221 Length(..) | Percentage(..) | Number(..) | Angle(..) | Time(..)
222 | Resolution(..) => {},
223 }
224 debug_unreachable!("Forgot to handle unit in try_sum_in_place()")
225 },
226 }
227
228 Ok(())
229 }
230
231 fn try_product_in_place(&mut self, other: &mut Self) -> bool {
232 if let Self::Number(ref mut left) = *self {
233 if let Self::Number(ref right) = *other {
234 *left *= *right;
236 true
237 } else {
238 if other.map(|v| v * *left).is_ok() {
241 std::mem::swap(self, other);
242 true
243 } else {
244 false
245 }
246 }
247 } else if let Self::Number(ref right) = *other {
248 self.map(|v| v * *right).is_ok()
251 } else {
252 false
254 }
255 }
256
257 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
258 where
259 O: Fn(f32, f32) -> f32,
260 {
261 use self::ComputedLeaf::*;
262 if std::mem::discriminant(self) != std::mem::discriminant(other) {
263 return Err(());
264 }
265 Ok(match (self, other) {
266 (Length(one), Length(other)) => Length(super::Length::new(op(one.px(), other.px()))),
267 (Percentage(one), Percentage(other)) => Self::Percentage(CalcPercentageLeaf::new(
268 op(one.get(), other.get()),
269 one.combined_hint(other),
270 )),
271 (&Number(one), &Number(other)) => Self::Number(op(one, other)),
272 (Angle(one), Angle(other)) => Self::Angle(super::Angle::from_degrees(op(
273 one.degrees(),
274 other.degrees(),
275 ))),
276 (Time(one), Time(other)) => Self::Time(super::Time::from_seconds(op(
277 one.seconds(),
278 other.seconds(),
279 ))),
280 (Resolution(one), Resolution(other)) => {
281 Self::Resolution(super::Resolution::from_dppx(op(one.dppx(), other.dppx())))
282 },
283 _ => unsafe {
284 match *self {
285 Length(..) | Percentage(..) | Number(..) | Angle(..) | Time(..)
286 | Resolution(..) => {},
287 }
288 debug_unreachable!("Forgot to handle unit in try_op()")
289 },
290 })
291 }
292
293 fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {
294 let _: () = match self {
295 Self::Length(value) => {
296 *value = Length::new(op(value.px()));
297 },
298 Self::Percentage(p) => {
299 *p = CalcPercentageLeaf::new(op(p.get()), p.hint);
300 },
301 Self::Number(value) => {
302 *value = op(*value);
303 },
304 Self::Angle(value) => {
305 *value = Angle::from_degrees(op(value.degrees()));
306 },
307 Self::Time(value) => {
308 *value = Time::from_seconds(op(value.seconds()));
309 },
310 Self::Resolution(value) => {
311 *value = Resolution::from_dppx(op(value.dppx()));
312 },
313 };
314 Ok(())
315 }
316
317 fn simplify(&mut self) -> SimplificationResult {
318 SimplificationResult::Unchanged
319 }
320
321 fn sort_key(&self) -> calc::SortKey {
322 match *self {
323 Self::Length(..) => calc::SortKey::Px,
324 Self::Percentage(..) => calc::SortKey::Percentage,
325 Self::Number(..) => calc::SortKey::Number,
326 Self::Angle(..) => calc::SortKey::Deg,
327 Self::Time(..) => calc::SortKey::S,
328 Self::Resolution(..) => calc::SortKey::Dppx,
329 }
330 }
331}