1use core::ops::Range;
6
7use crate::vm::{Any, IVm};
8
9impl<A: IVm> Any<A> {
10 pub fn to_integer_or_infinity(self) -> Result<f64, Any<A>> {
14 Ok(self.to_number()?.to_integer_or_infinity().into())
15 }
16}
17
18pub(crate) fn relative<A: IVm>(index: Any<A>, len: u32) -> Result<f64, Any<A>> {
25 let relative = index.to_integer_or_infinity()?;
26 Ok(if relative >= 0.0 {
27 relative
28 } else {
29 f64::from(len) + relative
30 })
31}
32
33pub(crate) fn clamped(k: f64, len: u32) -> u32 {
36 k.clamp(0.0, f64::from(len)) as u32
37}
38
39pub(crate) fn in_range(k: f64, len: u32) -> Option<u32> {
42 (0.0..f64::from(len)).contains(&k).then_some(k as u32)
43}
44
45pub(crate) fn relative_range<A: IVm>(
49 start: Any<A>,
50 end: Any<A>,
51 len: u32,
52) -> Result<Range<u32>, Any<A>> {
53 let from = clamped(relative(start, len)?, len);
54 let to = end
55 .defined()
56 .map(|e| relative(e, len))
57 .transpose()?
58 .map_or(len, |k| clamped(k, len));
59 Ok(from..to.max(from))
60}
61
62#[cfg(test)]
63mod tests {
64 use super::{clamped, in_range, relative, relative_range};
65 use crate::{
66 naive::Naive,
67 vm::{Any, Nullish, ToAny, error, unstable::bigint_any},
68 };
69
70 type A = Naive;
71
72 fn r(v: Any<A>) -> Result<f64, Any<A>> {
73 relative(v, 3)
74 }
75
76 #[test]
77 fn to_integer_or_infinity() {
78 assert_eq!(1.7.to_any::<A>().to_integer_or_infinity(), Ok(1.0));
79 assert_eq!((-1.0f64).to_any::<A>().to_integer_or_infinity(), Ok(-1.0));
80 assert_eq!(Any::<A>::undefined().to_integer_or_infinity(), Ok(0.0));
81 let one: Any<A> = bigint_any(1);
82 assert_eq!(one.to_integer_or_infinity(), Err(error::bigint_to_number()));
83 }
84
85 #[test]
86 fn from_the_start_and_the_end() {
87 assert_eq!(r(1.0.to_any()), Ok(1.0));
88 assert_eq!(r((-1.0f64).to_any()), Ok(2.0));
89 assert_eq!(r((-4.0f64).to_any()), Ok(-1.0));
90 assert_eq!(r(1.7.to_any()), Ok(1.0));
91 assert_eq!(r("2".into()), Ok(2.0));
92 assert_eq!(r(Any::undefined()), Ok(0.0));
93 assert_eq!(r(f64::INFINITY.to_any()), Ok(f64::INFINITY));
94 assert_eq!(r(f64::NEG_INFINITY.to_any()), Ok(f64::NEG_INFINITY));
95 }
96
97 #[test]
98 fn bigint_throws() {
99 assert_eq!(r(bigint_any(1)), Err(error::bigint_to_number()));
100 }
101
102 #[test]
103 fn clamps_into_the_collection() {
104 assert_eq!(clamped(-1.0, 3), 0);
105 assert_eq!(clamped(f64::NEG_INFINITY, 3), 0);
106 assert_eq!(clamped(2.0, 3), 2);
107 assert_eq!(clamped(4.0, 3), 3);
108 assert_eq!(clamped(f64::INFINITY, 3), 3);
109 }
110
111 #[test]
112 fn inside_the_collection() {
113 assert_eq!(in_range(0.0, 3), Some(0));
114 assert_eq!(in_range(2.0, 3), Some(2));
115 assert_eq!(in_range(3.0, 3), None);
116 assert_eq!(in_range(-1.0, 3), None);
117 assert_eq!(in_range(f64::INFINITY, 3), None);
118 assert_eq!(in_range(f64::NEG_INFINITY, 3), None);
119 assert_eq!(in_range(0.0, 0), None);
120 }
121
122 #[test]
123 fn ranges() {
124 let range = |start: f64, end: Any<A>| relative_range(start.to_any(), end, 3);
125 assert_eq!(range(1.0, Any::undefined()), Ok(1..3));
126 assert_eq!(range(-2.0, (-1.0f64).to_any()), Ok(1..2));
127 assert_eq!(range(-9.0, 9.0.to_any()), Ok(0..3));
128 assert_eq!(range(2.0, 1.0.to_any()), Ok(2..2));
129 assert_eq!(range(0.0, Nullish::Null.to_any()), Ok(0..0));
130 assert_eq!(range(0.0, bigint_any(1)), Err(error::bigint_to_number()));
131 }
132}