nu_protocol/value/
range.rs

1//! A Range is an iterator over integers or floats.
2
3use crate::{ShellError, Signals, Span, Value, ast::RangeInclusion};
4use core::ops::Bound;
5use serde::{Deserialize, Serialize};
6use std::{cmp::Ordering, fmt::Display};
7
8mod int_range {
9    use crate::{FromValue, ShellError, Signals, Span, Value, ast::RangeInclusion};
10    use serde::{Deserialize, Serialize};
11    use std::{cmp::Ordering, fmt::Display, ops::Bound};
12
13    use super::Range;
14
15    #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
16    pub struct IntRange {
17        pub(crate) start: i64,
18        pub(crate) step: i64,
19        pub(crate) end: Bound<i64>,
20    }
21
22    impl IntRange {
23        pub fn new(
24            start: Value,
25            next: Value,
26            end: Value,
27            inclusion: RangeInclusion,
28            span: Span,
29        ) -> Result<Self, ShellError> {
30            fn to_int(value: Value) -> Result<Option<i64>, ShellError> {
31                match value {
32                    Value::Int { val, .. } => Ok(Some(val)),
33                    Value::Nothing { .. } => Ok(None),
34                    val => Err(ShellError::CantConvert {
35                        to_type: "int".into(),
36                        from_type: val.get_type().to_string(),
37                        span: val.span(),
38                        help: None,
39                    }),
40                }
41            }
42
43            let start = to_int(start)?.unwrap_or(0);
44
45            let next_span = next.span();
46            let next = to_int(next)?;
47            if next.is_some_and(|next| next == start) {
48                return Err(ShellError::CannotCreateRange { span: next_span });
49            }
50
51            let end = to_int(end)?;
52
53            let step = match (next, end) {
54                (Some(next), Some(end)) => {
55                    if (next < start) != (end < start) {
56                        return Err(ShellError::CannotCreateRange { span });
57                    }
58                    next - start
59                }
60                (Some(next), None) => next - start,
61                (None, Some(end)) => {
62                    if end < start {
63                        -1
64                    } else {
65                        1
66                    }
67                }
68                (None, None) => 1,
69            };
70
71            let end = if let Some(end) = end {
72                match inclusion {
73                    RangeInclusion::Inclusive => Bound::Included(end),
74                    RangeInclusion::RightExclusive => Bound::Excluded(end),
75                }
76            } else {
77                Bound::Unbounded
78            };
79
80            Ok(Self { start, step, end })
81        }
82
83        pub fn start(&self) -> i64 {
84            self.start
85        }
86
87        // Resolves the absolute start position given the length of the input value
88        pub fn absolute_start(&self, len: u64) -> u64 {
89            match self.start {
90                start if start < 0 => len.saturating_sub(start.unsigned_abs()),
91                start => len.min(start.unsigned_abs()),
92            }
93        }
94
95        /// Returns the distance between the start and end of the range
96        /// The result will always be 0 or positive
97        pub fn distance(&self) -> Bound<u64> {
98            match self.end {
99                Bound::Unbounded => Bound::Unbounded,
100                Bound::Included(end) | Bound::Excluded(end) if self.start > end => {
101                    Bound::Excluded(0)
102                }
103                Bound::Included(end) => Bound::Included((end - self.start) as u64),
104                Bound::Excluded(end) => Bound::Excluded((end - self.start) as u64),
105            }
106        }
107
108        pub fn end(&self) -> Bound<i64> {
109            self.end
110        }
111
112        pub fn absolute_end(&self, len: u64) -> Bound<u64> {
113            match self.end {
114                Bound::Unbounded => Bound::Unbounded,
115                Bound::Included(i) => match i {
116                    _ if len == 0 => Bound::Excluded(0),
117                    i if i < 0 => Bound::Excluded(len.saturating_sub((i + 1).unsigned_abs())),
118                    i => Bound::Included((len.saturating_sub(1)).min(i.unsigned_abs())),
119                },
120                Bound::Excluded(i) => Bound::Excluded(match i {
121                    i if i < 0 => len.saturating_sub(i.unsigned_abs()),
122                    i => len.min(i.unsigned_abs()),
123                }),
124            }
125        }
126
127        pub fn absolute_bounds(&self, len: usize) -> (usize, Bound<usize>) {
128            let start = self.absolute_start(len as u64) as usize;
129            let end = self.absolute_end(len as u64).map(|e| e as usize);
130            match end {
131                Bound::Excluded(end) | Bound::Included(end) if end < start => {
132                    (start, Bound::Excluded(start))
133                }
134                Bound::Excluded(end) => (start, Bound::Excluded(end)),
135                Bound::Included(end) => (start, Bound::Included(end)),
136                Bound::Unbounded => (start, Bound::Unbounded),
137            }
138        }
139
140        pub fn step(&self) -> i64 {
141            self.step
142        }
143
144        pub fn is_unbounded(&self) -> bool {
145            self.end == Bound::Unbounded
146        }
147
148        pub fn is_relative(&self) -> bool {
149            self.is_start_relative() || self.is_end_relative()
150        }
151
152        pub fn is_start_relative(&self) -> bool {
153            self.start < 0
154        }
155
156        pub fn is_end_relative(&self) -> bool {
157            match self.end {
158                Bound::Included(end) | Bound::Excluded(end) => end < 0,
159                _ => false,
160            }
161        }
162
163        pub fn contains(&self, value: i64) -> bool {
164            if self.step < 0 {
165                // Decreasing range
166                if value > self.start {
167                    return false;
168                }
169                match self.end {
170                    Bound::Included(end) if value < end => return false,
171                    Bound::Excluded(end) if value <= end => return false,
172                    _ => {}
173                };
174            } else {
175                // Increasing range
176                if value < self.start {
177                    return false;
178                }
179                match self.end {
180                    Bound::Included(end) if value > end => return false,
181                    Bound::Excluded(end) if value >= end => return false,
182                    _ => {}
183                };
184            }
185            (value - self.start) % self.step == 0
186        }
187
188        pub fn into_range_iter(self, signals: Signals) -> Iter {
189            Iter {
190                current: Some(self.start),
191                step: self.step,
192                end: self.end,
193                signals,
194            }
195        }
196    }
197
198    impl Ord for IntRange {
199        fn cmp(&self, other: &Self) -> Ordering {
200            // Ranges are compared roughly according to their list representation.
201            // Compare in order:
202            // - the head element (start)
203            // - the tail elements (step)
204            // - the length (end)
205            self.start
206                .cmp(&other.start)
207                .then(self.step.cmp(&other.step))
208                .then_with(|| match (self.end, other.end) {
209                    (Bound::Included(l), Bound::Included(r))
210                    | (Bound::Excluded(l), Bound::Excluded(r)) => {
211                        let ord = l.cmp(&r);
212                        if self.step < 0 { ord.reverse() } else { ord }
213                    }
214                    (Bound::Included(l), Bound::Excluded(r)) => match l.cmp(&r) {
215                        Ordering::Equal => Ordering::Greater,
216                        ord if self.step < 0 => ord.reverse(),
217                        ord => ord,
218                    },
219                    (Bound::Excluded(l), Bound::Included(r)) => match l.cmp(&r) {
220                        Ordering::Equal => Ordering::Less,
221                        ord if self.step < 0 => ord.reverse(),
222                        ord => ord,
223                    },
224                    (Bound::Included(_), Bound::Unbounded) => Ordering::Less,
225                    (Bound::Excluded(_), Bound::Unbounded) => Ordering::Less,
226                    (Bound::Unbounded, Bound::Included(_)) => Ordering::Greater,
227                    (Bound::Unbounded, Bound::Excluded(_)) => Ordering::Greater,
228                    (Bound::Unbounded, Bound::Unbounded) => Ordering::Equal,
229                })
230        }
231    }
232
233    impl PartialOrd for IntRange {
234        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
235            Some(self.cmp(other))
236        }
237    }
238
239    impl PartialEq for IntRange {
240        fn eq(&self, other: &Self) -> bool {
241            self.start == other.start && self.step == other.step && self.end == other.end
242        }
243    }
244
245    impl Eq for IntRange {}
246
247    impl Display for IntRange {
248        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249            write!(f, "{}..", self.start)?;
250            if self.step != 1 {
251                write!(f, "{}..", self.start + self.step)?;
252            }
253            match self.end {
254                Bound::Included(end) => write!(f, "{end}"),
255                Bound::Excluded(end) => write!(f, "<{end}"),
256                Bound::Unbounded => Ok(()),
257            }
258        }
259    }
260
261    impl FromValue for IntRange {
262        fn from_value(v: Value) -> Result<Self, ShellError> {
263            let span = v.span();
264            let range = Range::from_value(v)?;
265            match range {
266                Range::IntRange(v) => Ok(v),
267                Range::FloatRange(_) => Err(ShellError::TypeMismatch {
268                    err_message: "expected an int range".into(),
269                    span,
270                }),
271            }
272        }
273    }
274
275    pub struct Iter {
276        current: Option<i64>,
277        step: i64,
278        end: Bound<i64>,
279        signals: Signals,
280    }
281
282    impl Iterator for Iter {
283        type Item = i64;
284
285        fn next(&mut self) -> Option<Self::Item> {
286            if let Some(current) = self.current {
287                let not_end = match (self.step < 0, self.end) {
288                    (true, Bound::Included(end)) => current >= end,
289                    (true, Bound::Excluded(end)) => current > end,
290                    (false, Bound::Included(end)) => current <= end,
291                    (false, Bound::Excluded(end)) => current < end,
292                    (_, Bound::Unbounded) => true, // will stop once integer overflows
293                };
294
295                if not_end && !self.signals.interrupted() {
296                    self.current = current.checked_add(self.step);
297                    Some(current)
298                } else {
299                    self.current = None;
300                    None
301                }
302            } else {
303                None
304            }
305        }
306    }
307}
308
309mod float_range {
310    use crate::{IntRange, Range, ShellError, Signals, Span, Value, ast::RangeInclusion};
311    use nu_utils::ObviousFloat;
312    use serde::{Deserialize, Serialize};
313    use std::{cmp::Ordering, fmt::Display, ops::Bound};
314
315    #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
316    pub struct FloatRange {
317        pub(crate) start: f64,
318        pub(crate) step: f64,
319        pub(crate) end: Bound<f64>,
320    }
321
322    impl FloatRange {
323        pub fn new(
324            start: Value,
325            next: Value,
326            end: Value,
327            inclusion: RangeInclusion,
328            span: Span,
329        ) -> Result<Self, ShellError> {
330            fn to_float(value: Value) -> Result<Option<f64>, ShellError> {
331                match value {
332                    Value::Float { val, .. } => Ok(Some(val)),
333                    Value::Int { val, .. } => Ok(Some(val as f64)),
334                    Value::Nothing { .. } => Ok(None),
335                    val => Err(ShellError::CantConvert {
336                        to_type: "float".into(),
337                        from_type: val.get_type().to_string(),
338                        span: val.span(),
339                        help: None,
340                    }),
341                }
342            }
343
344            // `start` must be finite (not NaN or infinity).
345            // `next` must be finite and not equal to `start`.
346            // `end` must not be NaN (but can be infinite).
347            //
348            // TODO: better error messages for the restrictions above
349
350            let start_span = start.span();
351            let start = to_float(start)?.unwrap_or(0.0);
352            if !start.is_finite() {
353                return Err(ShellError::CannotCreateRange { span: start_span });
354            }
355
356            let end_span = end.span();
357            let end = to_float(end)?;
358            if end.is_some_and(f64::is_nan) {
359                return Err(ShellError::CannotCreateRange { span: end_span });
360            }
361
362            let next_span = next.span();
363            let next = to_float(next)?;
364            if next.is_some_and(|next| next == start || !next.is_finite()) {
365                return Err(ShellError::CannotCreateRange { span: next_span });
366            }
367
368            let step = match (next, end) {
369                (Some(next), Some(end)) => {
370                    if (next < start) != (end < start) {
371                        return Err(ShellError::CannotCreateRange { span });
372                    }
373                    next - start
374                }
375                (Some(next), None) => next - start,
376                (None, Some(end)) => {
377                    if end < start {
378                        -1.0
379                    } else {
380                        1.0
381                    }
382                }
383                (None, None) => 1.0,
384            };
385
386            let end = if let Some(end) = end {
387                if end.is_infinite() {
388                    Bound::Unbounded
389                } else {
390                    match inclusion {
391                        RangeInclusion::Inclusive => Bound::Included(end),
392                        RangeInclusion::RightExclusive => Bound::Excluded(end),
393                    }
394                }
395            } else {
396                Bound::Unbounded
397            };
398
399            Ok(Self { start, step, end })
400        }
401
402        pub fn start(&self) -> f64 {
403            self.start
404        }
405
406        pub fn end(&self) -> Bound<f64> {
407            self.end
408        }
409
410        pub fn step(&self) -> f64 {
411            self.step
412        }
413
414        pub fn is_unbounded(&self) -> bool {
415            self.end == Bound::Unbounded
416        }
417
418        pub fn contains(&self, value: f64) -> bool {
419            if self.step < 0.0 {
420                // Decreasing range
421                if value > self.start {
422                    return false;
423                }
424                match self.end {
425                    Bound::Included(end) if value <= end => return false,
426                    Bound::Excluded(end) if value < end => return false,
427                    _ => {}
428                };
429            } else {
430                // Increasing range
431                if value < self.start {
432                    return false;
433                }
434                match self.end {
435                    Bound::Included(end) if value >= end => return false,
436                    Bound::Excluded(end) if value > end => return false,
437                    _ => {}
438                };
439            }
440            ((value - self.start) % self.step).abs() < f64::EPSILON
441        }
442
443        pub fn into_range_iter(self, signals: Signals) -> Iter {
444            Iter {
445                start: self.start,
446                step: self.step,
447                end: self.end,
448                iter: Some(0),
449                signals,
450            }
451        }
452    }
453
454    impl Ord for FloatRange {
455        fn cmp(&self, other: &Self) -> Ordering {
456            fn float_cmp(a: f64, b: f64) -> Ordering {
457                // There is no way a `FloatRange` can have NaN values:
458                // - `FloatRange::new` ensures no values are NaN.
459                // - `From<IntRange> for FloatRange` cannot give NaNs either.
460                // - There are no other ways to create a `FloatRange`.
461                // - There is no way to modify values of a `FloatRange`.
462                a.partial_cmp(&b).expect("not NaN")
463            }
464
465            // Ranges are compared roughly according to their list representation.
466            // Compare in order:
467            // - the head element (start)
468            // - the tail elements (step)
469            // - the length (end)
470            float_cmp(self.start, other.start)
471                .then(float_cmp(self.step, other.step))
472                .then_with(|| match (self.end, other.end) {
473                    (Bound::Included(l), Bound::Included(r))
474                    | (Bound::Excluded(l), Bound::Excluded(r)) => {
475                        let ord = float_cmp(l, r);
476                        if self.step < 0.0 { ord.reverse() } else { ord }
477                    }
478                    (Bound::Included(l), Bound::Excluded(r)) => match float_cmp(l, r) {
479                        Ordering::Equal => Ordering::Greater,
480                        ord if self.step < 0.0 => ord.reverse(),
481                        ord => ord,
482                    },
483                    (Bound::Excluded(l), Bound::Included(r)) => match float_cmp(l, r) {
484                        Ordering::Equal => Ordering::Less,
485                        ord if self.step < 0.0 => ord.reverse(),
486                        ord => ord,
487                    },
488                    (Bound::Included(_), Bound::Unbounded) => Ordering::Less,
489                    (Bound::Excluded(_), Bound::Unbounded) => Ordering::Less,
490                    (Bound::Unbounded, Bound::Included(_)) => Ordering::Greater,
491                    (Bound::Unbounded, Bound::Excluded(_)) => Ordering::Greater,
492                    (Bound::Unbounded, Bound::Unbounded) => Ordering::Equal,
493                })
494        }
495    }
496
497    impl PartialOrd for FloatRange {
498        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
499            Some(self.cmp(other))
500        }
501    }
502
503    impl PartialEq for FloatRange {
504        fn eq(&self, other: &Self) -> bool {
505            self.start == other.start && self.step == other.step && self.end == other.end
506        }
507    }
508
509    impl Eq for FloatRange {}
510
511    impl Display for FloatRange {
512        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
513            write!(f, "{}..", ObviousFloat(self.start))?;
514            if self.step != 1f64 {
515                write!(f, "{}..", ObviousFloat(self.start + self.step))?;
516            }
517            match self.end {
518                Bound::Included(end) => write!(f, "{}", ObviousFloat(end)),
519                Bound::Excluded(end) => write!(f, "<{}", ObviousFloat(end)),
520                Bound::Unbounded => Ok(()),
521            }
522        }
523    }
524
525    impl From<IntRange> for FloatRange {
526        fn from(range: IntRange) -> Self {
527            Self {
528                start: range.start as f64,
529                step: range.step as f64,
530                end: match range.end {
531                    Bound::Included(b) => Bound::Included(b as f64),
532                    Bound::Excluded(b) => Bound::Excluded(b as f64),
533                    Bound::Unbounded => Bound::Unbounded,
534                },
535            }
536        }
537    }
538
539    impl From<Range> for FloatRange {
540        fn from(range: Range) -> Self {
541            match range {
542                Range::IntRange(range) => range.into(),
543                Range::FloatRange(range) => range,
544            }
545        }
546    }
547
548    pub struct Iter {
549        start: f64,
550        step: f64,
551        end: Bound<f64>,
552        iter: Option<u64>,
553        signals: Signals,
554    }
555
556    impl Iterator for Iter {
557        type Item = f64;
558
559        fn next(&mut self) -> Option<Self::Item> {
560            if let Some(iter) = self.iter {
561                let current = self.start + self.step * iter as f64;
562
563                let not_end = match (self.step < 0.0, self.end) {
564                    (true, Bound::Included(end)) => current >= end,
565                    (true, Bound::Excluded(end)) => current > end,
566                    (false, Bound::Included(end)) => current <= end,
567                    (false, Bound::Excluded(end)) => current < end,
568                    (_, Bound::Unbounded) => current.is_finite(),
569                };
570
571                if not_end && !self.signals.interrupted() {
572                    self.iter = iter.checked_add(1);
573                    Some(current)
574                } else {
575                    self.iter = None;
576                    None
577                }
578            } else {
579                None
580            }
581        }
582    }
583}
584
585pub use float_range::FloatRange;
586pub use int_range::IntRange;
587
588#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
589pub enum Range {
590    IntRange(IntRange),
591    FloatRange(FloatRange),
592}
593
594impl Range {
595    pub fn new(
596        start: Value,
597        next: Value,
598        end: Value,
599        inclusion: RangeInclusion,
600        span: Span,
601    ) -> Result<Self, ShellError> {
602        // promote to float range if any Value is float
603        if matches!(start, Value::Float { .. })
604            || matches!(next, Value::Float { .. })
605            || matches!(end, Value::Float { .. })
606        {
607            FloatRange::new(start, next, end, inclusion, span).map(Self::FloatRange)
608        } else {
609            IntRange::new(start, next, end, inclusion, span).map(Self::IntRange)
610        }
611    }
612
613    pub fn contains(&self, value: &Value) -> bool {
614        match (self, value) {
615            (Self::IntRange(range), Value::Int { val, .. }) => range.contains(*val),
616            (Self::IntRange(range), Value::Float { val, .. }) => {
617                FloatRange::from(*range).contains(*val)
618            }
619            (Self::FloatRange(range), Value::Int { val, .. }) => range.contains(*val as f64),
620            (Self::FloatRange(range), Value::Float { val, .. }) => range.contains(*val),
621            _ => false,
622        }
623    }
624
625    pub fn is_bounded(&self) -> bool {
626        match self {
627            Range::IntRange(range) => range.end() != Bound::<i64>::Unbounded,
628            Range::FloatRange(range) => range.end() != Bound::<f64>::Unbounded,
629        }
630    }
631
632    pub fn into_range_iter(self, span: Span, signals: Signals) -> Iter {
633        match self {
634            Range::IntRange(range) => Iter::IntIter(range.into_range_iter(signals), span),
635            Range::FloatRange(range) => Iter::FloatIter(range.into_range_iter(signals), span),
636        }
637    }
638}
639
640impl Ord for Range {
641    fn cmp(&self, other: &Self) -> Ordering {
642        match (self, other) {
643            (Range::IntRange(l), Range::IntRange(r)) => l.cmp(r),
644            (Range::FloatRange(l), Range::FloatRange(r)) => l.cmp(r),
645            (Range::IntRange(int), Range::FloatRange(float)) => FloatRange::from(*int).cmp(float),
646            (Range::FloatRange(float), Range::IntRange(int)) => float.cmp(&FloatRange::from(*int)),
647        }
648    }
649}
650
651impl PartialOrd for Range {
652    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
653        Some(self.cmp(other))
654    }
655}
656
657impl PartialEq for Range {
658    fn eq(&self, other: &Self) -> bool {
659        match (self, other) {
660            (Range::IntRange(l), Range::IntRange(r)) => l == r,
661            (Range::FloatRange(l), Range::FloatRange(r)) => l == r,
662            (Range::IntRange(int), Range::FloatRange(float)) => FloatRange::from(*int) == *float,
663            (Range::FloatRange(float), Range::IntRange(int)) => *float == FloatRange::from(*int),
664        }
665    }
666}
667
668impl Eq for Range {}
669
670impl Display for Range {
671    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
672        match self {
673            Range::IntRange(range) => write!(f, "{range}"),
674            Range::FloatRange(range) => write!(f, "{range}"),
675        }
676    }
677}
678
679impl From<IntRange> for Range {
680    fn from(range: IntRange) -> Self {
681        Self::IntRange(range)
682    }
683}
684
685impl From<FloatRange> for Range {
686    fn from(range: FloatRange) -> Self {
687        Self::FloatRange(range)
688    }
689}
690
691pub enum Iter {
692    IntIter(int_range::Iter, Span),
693    FloatIter(float_range::Iter, Span),
694}
695
696impl Iterator for Iter {
697    type Item = Value;
698
699    fn next(&mut self) -> Option<Self::Item> {
700        match self {
701            Iter::IntIter(iter, span) => iter.next().map(|val| Value::int(val, *span)),
702            Iter::FloatIter(iter, span) => iter.next().map(|val| Value::float(val, *span)),
703        }
704    }
705}