Skip to main content

style/values/computed/
calc.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Computed-value calc() leaf types.
6
7use super::{Angle, Length, Number, Percentage, Resolution, Time};
8use crate::derives::*;
9use crate::values::generics::calc::{
10    self, CalcUnits, PositivePercentageBasis, SimplificationResult,
11};
12use crate::Zero;
13use debug_unreachable::debug_unreachable;
14use serde::{Deserialize, Serialize};
15
16/// The computed leaf of a calc() expression.
17#[derive(
18    Clone,
19    Debug,
20    Deserialize,
21    MallocSizeOf,
22    PartialEq,
23    Serialize,
24    ToAnimatedZero,
25    ToCss,
26    ToResolvedValue,
27    ToTyped,
28)]
29#[allow(missing_docs)]
30#[repr(u8)]
31pub enum ComputedLeaf {
32    Length(Length),
33    Percentage(Percentage),
34    Number(Number),
35    Angle(Angle),
36    Time(Time),
37    Resolution(Resolution),
38}
39
40impl ComputedLeaf {
41    pub(super) fn is_zero_length(&self) -> bool {
42        match *self {
43            Self::Length(ref l) => l.is_zero(),
44            Self::Percentage(..)
45            | Self::Number(..)
46            | Self::Angle(..)
47            | Self::Time(..)
48            | Self::Resolution(..) => false,
49        }
50    }
51}
52
53impl calc::CalcNodeLeaf for ComputedLeaf {
54    fn unit(&self) -> CalcUnits {
55        match self {
56            Self::Length(_) => CalcUnits::LENGTH,
57            Self::Percentage(_) => CalcUnits::PERCENTAGE,
58            Self::Number(_) => CalcUnits::empty(),
59            Self::Angle(_) => CalcUnits::ANGLE,
60            Self::Time(_) => CalcUnits::TIME,
61            Self::Resolution(_) => CalcUnits::RESOLUTION,
62        }
63    }
64
65    fn unitless_value(&self) -> Option<f32> {
66        Some(match *self {
67            Self::Length(ref l) => l.px(),
68            Self::Percentage(ref p) => p.0,
69            Self::Number(n) => n,
70            Self::Angle(ref a) => a.degrees(),
71            Self::Time(ref t) => t.seconds(),
72            Self::Resolution(ref r) => r.dppx(),
73        })
74    }
75
76    fn new_number(value: f32) -> Self {
77        Self::Number(value)
78    }
79
80    fn as_number(&self) -> Option<f32> {
81        match *self {
82            Self::Length(_)
83            | Self::Percentage(_)
84            | Self::Angle(_)
85            | Self::Time(_)
86            | Self::Resolution(_) => None,
87            Self::Number(value) => Some(value),
88        }
89    }
90
91    fn as_angle_radians(&self) -> Option<f32> {
92        match *self {
93            Self::Angle(a) => Some(a.radians()),
94            _ => None,
95        }
96    }
97
98    fn new_angle_from_radians(radians: f32) -> Self {
99        Self::Angle(Angle::from_radians(radians))
100    }
101
102    fn compare(&self, other: &Self, basis: PositivePercentageBasis) -> Option<std::cmp::Ordering> {
103        use self::ComputedLeaf::*;
104        if std::mem::discriminant(self) != std::mem::discriminant(other) {
105            return None;
106        }
107
108        if matches!(self, Percentage(..)) && matches!(basis, PositivePercentageBasis::Unknown) {
109            return None;
110        }
111
112        let Ok(self_negative) = self.is_negative() else {
113            return None;
114        };
115        let Ok(other_negative) = other.is_negative() else {
116            return None;
117        };
118        if self_negative != other_negative {
119            return Some(if self_negative {
120                std::cmp::Ordering::Less
121            } else {
122                std::cmp::Ordering::Greater
123            });
124        }
125
126        match (self, other) {
127            (&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
128            (&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),
129            (&Number(ref one), &Number(ref other)) => one.partial_cmp(other),
130            (&Angle(ref one), &Angle(ref other)) => one.partial_cmp(other),
131            (&Time(ref one), &Time(ref other)) => one.partial_cmp(other),
132            (&Resolution(ref one), &Resolution(ref other)) => one.partial_cmp(other),
133            _ => unsafe {
134                match *self {
135                    Length(..) | Percentage(..) | Number(..) | Angle(..) | Time(..)
136                    | Resolution(..) => {},
137                }
138                debug_unreachable!("Forgot to handle unit in compare()")
139            },
140        }
141    }
142
143    fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
144        use self::ComputedLeaf::*;
145
146        // 0px plus anything else is equal to the right hand side.
147        if self.is_zero_length() {
148            *self = other.clone();
149            return Ok(());
150        }
151
152        if other.is_zero_length() {
153            return Ok(());
154        }
155
156        if std::mem::discriminant(self) != std::mem::discriminant(other) {
157            return Err(());
158        }
159
160        match (self, other) {
161            (&mut Length(ref mut one), &Length(ref other)) => {
162                *one += *other;
163            },
164            (&mut Percentage(ref mut one), &Percentage(ref other)) => {
165                one.0 += other.0;
166            },
167            (&mut Number(ref mut one), &Number(ref other)) => {
168                *one += *other;
169            },
170            (&mut Angle(ref mut one), &Angle(ref other)) => {
171                *one += *other;
172            },
173            (&mut Time(ref mut one), &Time(ref other)) => {
174                *one += *other;
175            },
176            (&mut Resolution(ref mut one), &Resolution(ref other)) => {
177                *one += *other;
178            },
179            _ => unsafe {
180                match *other {
181                    Length(..) | Percentage(..) | Number(..) | Angle(..) | Time(..)
182                    | Resolution(..) => {},
183                }
184                debug_unreachable!("Forgot to handle unit in try_sum_in_place()")
185            },
186        }
187
188        Ok(())
189    }
190
191    fn try_product_in_place(&mut self, other: &mut Self) -> bool {
192        if let Self::Number(ref mut left) = *self {
193            if let Self::Number(ref right) = *other {
194                // Both sides are numbers, so we can just modify the left side.
195                *left *= *right;
196                true
197            } else {
198                // The right side is not a number, so the result should be in the units of the right
199                // side.
200                if other.map(|v| v * *left).is_ok() {
201                    std::mem::swap(self, other);
202                    true
203                } else {
204                    false
205                }
206            }
207        } else if let Self::Number(ref right) = *other {
208            // The left side is not a number, but the right side is, so the result is the left
209            // side unit.
210            self.map(|v| v * *right).is_ok()
211        } else {
212            // Neither side is a number, so a product is not possible.
213            false
214        }
215    }
216
217    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
218    where
219        O: Fn(f32, f32) -> f32,
220    {
221        use self::ComputedLeaf::*;
222        if std::mem::discriminant(self) != std::mem::discriminant(other) {
223            return Err(());
224        }
225        Ok(match (self, other) {
226            (&Length(ref one), &Length(ref other)) => {
227                Length(super::Length::new(op(one.px(), other.px())))
228            },
229            (&Percentage(one), &Percentage(other)) => {
230                Self::Percentage(super::Percentage(op(one.0, other.0)))
231            },
232            (&Number(one), &Number(other)) => Self::Number(op(one, other)),
233            (&Angle(ref one), &Angle(ref other)) => Self::Angle(super::Angle::from_degrees(op(
234                one.degrees(),
235                other.degrees(),
236            ))),
237            (&Time(ref one), &Time(ref other)) => Self::Time(super::Time::from_seconds(op(
238                one.seconds(),
239                other.seconds(),
240            ))),
241            (&Resolution(ref one), &Resolution(ref other)) => {
242                Self::Resolution(super::Resolution::from_dppx(op(one.dppx(), other.dppx())))
243            },
244            _ => unsafe {
245                match *self {
246                    Length(..) | Percentage(..) | Number(..) | Angle(..) | Time(..)
247                    | Resolution(..) => {},
248                }
249                debug_unreachable!("Forgot to handle unit in try_op()")
250            },
251        })
252    }
253
254    fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {
255        Ok(match self {
256            Self::Length(value) => {
257                *value = Length::new(op(value.px()));
258            },
259            Self::Percentage(value) => {
260                *value = Percentage(op(value.0));
261            },
262            Self::Number(value) => {
263                *value = op(*value);
264            },
265            Self::Angle(value) => {
266                *value = Angle::from_degrees(op(value.degrees()));
267            },
268            Self::Time(value) => {
269                *value = Time::from_seconds(op(value.seconds()));
270            },
271            Self::Resolution(value) => {
272                *value = Resolution::from_dppx(op(value.dppx()));
273            },
274        })
275    }
276
277    fn simplify(&mut self) -> SimplificationResult {
278        return SimplificationResult::Unchanged;
279    }
280
281    fn sort_key(&self) -> calc::SortKey {
282        match *self {
283            Self::Length(..) => calc::SortKey::Px,
284            Self::Percentage(..) => calc::SortKey::Percentage,
285            Self::Number(..) => calc::SortKey::Number,
286            Self::Angle(..) => calc::SortKey::Deg,
287            Self::Time(..) => calc::SortKey::S,
288            Self::Resolution(..) => calc::SortKey::Dppx,
289        }
290    }
291}