Skip to main content

boa_engine/value/
integer.rs

1use num_traits::{AsPrimitive, FromPrimitive};
2use std::cmp::Ordering;
3
4/// Represents the result of the `ToIntegerOrInfinity` operation
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
6pub enum IntegerOrInfinity {
7    /// Positive infinity.
8    PositiveInfinity,
9
10    /// An integer.
11    Integer(i64),
12
13    /// Negative infinity.
14    NegativeInfinity,
15}
16
17impl IntegerOrInfinity {
18    /// Clamps an `IntegerOrInfinity` between two `i64`, effectively converting
19    /// it to an i64.
20    ///
21    /// # Panics
22    ///
23    /// Panics if `min > max`.
24    #[must_use]
25    pub fn clamp_finite<I: Ord + AsPrimitive<i64> + FromPrimitive>(self, min: I, max: I) -> I {
26        assert!(min <= max);
27        match self {
28            Self::Integer(i) => {
29                I::from_i64(i.clamp(min.as_(), max.as_())).expect("`i` should already be clamped")
30            }
31            Self::PositiveInfinity => max,
32            Self::NegativeInfinity => min,
33        }
34    }
35
36    /// Gets the wrapped `i64` if the variant is an `Integer`.
37    #[must_use]
38    pub const fn as_integer(self) -> Option<i64> {
39        match self {
40            Self::Integer(i) => Some(i),
41            _ => None,
42        }
43    }
44}
45
46impl From<f64> for IntegerOrInfinity {
47    fn from(number: f64) -> Self {
48        // `ToIntegerOrInfinity ( argument )`
49        if number.is_nan() || number == 0.0 {
50            // 2. If number is NaN, +0𝔽, or -0𝔽, return 0.
51            Self::Integer(0)
52        } else if number == f64::INFINITY {
53            // 3. If number is +∞𝔽, return +∞.
54            Self::PositiveInfinity
55        } else if number == f64::NEG_INFINITY {
56            // 4. If number is -∞𝔽, return -∞.
57            Self::NegativeInfinity
58        } else {
59            // 5. Let integer be floor(abs(ℝ(number))).
60            // 6. If number < +0𝔽, set integer to -integer.
61            let integer = number.abs().floor().copysign(number) as i64;
62
63            // 7. Return integer.
64            Self::Integer(integer)
65        }
66    }
67}
68
69impl PartialEq<i64> for IntegerOrInfinity {
70    fn eq(&self, other: &i64) -> bool {
71        match self {
72            Self::Integer(i) => i == other,
73            _ => false,
74        }
75    }
76}
77
78impl PartialEq<IntegerOrInfinity> for i64 {
79    fn eq(&self, other: &IntegerOrInfinity) -> bool {
80        other.eq(self)
81    }
82}
83
84impl PartialOrd<i64> for IntegerOrInfinity {
85    fn partial_cmp(&self, other: &i64) -> Option<Ordering> {
86        match self {
87            Self::PositiveInfinity => Some(Ordering::Greater),
88            Self::Integer(i) => i.partial_cmp(other),
89            Self::NegativeInfinity => Some(Ordering::Less),
90        }
91    }
92}
93
94impl PartialOrd<IntegerOrInfinity> for i64 {
95    fn partial_cmp(&self, other: &IntegerOrInfinity) -> Option<Ordering> {
96        other.partial_cmp(self).map(Ordering::reverse)
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_eq() {
106        let int: i64 = 42;
107        let int_or_inf = IntegerOrInfinity::Integer(10);
108        assert!(int != int_or_inf);
109        assert!(int_or_inf != int);
110
111        let int: i64 = 10;
112        assert!(int == int_or_inf);
113        assert!(int_or_inf == int);
114    }
115
116    #[test]
117    fn test_ord() {
118        let int: i64 = 42;
119
120        let int_or_inf = IntegerOrInfinity::Integer(10);
121        assert!(int_or_inf < int);
122        assert!(int > int_or_inf);
123
124        let int_or_inf = IntegerOrInfinity::Integer(100);
125        assert!(int_or_inf > int);
126        assert!(int < int_or_inf);
127
128        let int_or_inf = IntegerOrInfinity::PositiveInfinity;
129        assert!(int_or_inf > int);
130        assert!(int < int_or_inf);
131
132        let int_or_inf = IntegerOrInfinity::NegativeInfinity;
133        assert!(int_or_inf < int);
134        assert!(int > int_or_inf);
135    }
136}