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::{
311        IntRange, Range, ShellError, Signals, Span, Value, ast::RangeInclusion,
312        format::ObviousFloat,
313    };
314    use serde::{Deserialize, Serialize};
315    use std::{cmp::Ordering, fmt::Display, ops::Bound};
316
317    #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
318    pub struct FloatRange {
319        pub(crate) start: f64,
320        pub(crate) step: f64,
321        pub(crate) end: Bound<f64>,
322    }
323
324    impl FloatRange {
325        pub fn new(
326            start: Value,
327            next: Value,
328            end: Value,
329            inclusion: RangeInclusion,
330            span: Span,
331        ) -> Result<Self, ShellError> {
332            fn to_float(value: Value) -> Result<Option<f64>, ShellError> {
333                match value {
334                    Value::Float { val, .. } => Ok(Some(val)),
335                    Value::Int { val, .. } => Ok(Some(val as f64)),
336                    Value::Nothing { .. } => Ok(None),
337                    val => Err(ShellError::CantConvert {
338                        to_type: "float".into(),
339                        from_type: val.get_type().to_string(),
340                        span: val.span(),
341                        help: None,
342                    }),
343                }
344            }
345
346            // `start` must be finite (not NaN or infinity).
347            // `next` must be finite and not equal to `start`.
348            // `end` must not be NaN (but can be infinite).
349            //
350            // TODO: better error messages for the restrictions above
351
352            let start_span = start.span();
353            let start = to_float(start)?.unwrap_or(0.0);
354            if !start.is_finite() {
355                return Err(ShellError::CannotCreateRange { span: start_span });
356            }
357
358            let end_span = end.span();
359            let end = to_float(end)?;
360            if end.is_some_and(f64::is_nan) {
361                return Err(ShellError::CannotCreateRange { span: end_span });
362            }
363
364            let next_span = next.span();
365            let next = to_float(next)?;
366            if next.is_some_and(|next| next == start || !next.is_finite()) {
367                return Err(ShellError::CannotCreateRange { span: next_span });
368            }
369
370            let step = match (next, end) {
371                (Some(next), Some(end)) => {
372                    if (next < start) != (end < start) {
373                        return Err(ShellError::CannotCreateRange { span });
374                    }
375                    next - start
376                }
377                (Some(next), None) => next - start,
378                (None, Some(end)) => {
379                    if end < start {
380                        -1.0
381                    } else {
382                        1.0
383                    }
384                }
385                (None, None) => 1.0,
386            };
387
388            let end = if let Some(end) = end {
389                if end.is_infinite() {
390                    Bound::Unbounded
391                } else {
392                    match inclusion {
393                        RangeInclusion::Inclusive => Bound::Included(end),
394                        RangeInclusion::RightExclusive => Bound::Excluded(end),
395                    }
396                }
397            } else {
398                Bound::Unbounded
399            };
400
401            Ok(Self { start, step, end })
402        }
403
404        pub fn start(&self) -> f64 {
405            self.start
406        }
407
408        pub fn end(&self) -> Bound<f64> {
409            self.end
410        }
411
412        pub fn step(&self) -> f64 {
413            self.step
414        }
415
416        pub fn is_unbounded(&self) -> bool {
417            self.end == Bound::Unbounded
418        }
419
420        pub fn contains(&self, value: f64) -> bool {
421            if self.step < 0.0 {
422                // Decreasing range
423                if value > self.start {
424                    return false;
425                }
426                match self.end {
427                    Bound::Included(end) if value <= end => return false,
428                    Bound::Excluded(end) if value < end => return false,
429                    _ => {}
430                };
431            } else {
432                // Increasing range
433                if value < self.start {
434                    return false;
435                }
436                match self.end {
437                    Bound::Included(end) if value >= end => return false,
438                    Bound::Excluded(end) if value > end => return false,
439                    _ => {}
440                };
441            }
442            ((value - self.start) % self.step).abs() < f64::EPSILON
443        }
444
445        pub fn into_range_iter(self, signals: Signals) -> Iter {
446            Iter {
447                start: self.start,
448                step: self.step,
449                end: self.end,
450                iter: Some(0),
451                signals,
452            }
453        }
454    }
455
456    impl Ord for FloatRange {
457        fn cmp(&self, other: &Self) -> Ordering {
458            fn float_cmp(a: f64, b: f64) -> Ordering {
459                // There is no way a `FloatRange` can have NaN values:
460                // - `FloatRange::new` ensures no values are NaN.
461                // - `From<IntRange> for FloatRange` cannot give NaNs either.
462                // - There are no other ways to create a `FloatRange`.
463                // - There is no way to modify values of a `FloatRange`.
464                a.partial_cmp(&b).expect("not NaN")
465            }
466
467            // Ranges are compared roughly according to their list representation.
468            // Compare in order:
469            // - the head element (start)
470            // - the tail elements (step)
471            // - the length (end)
472            float_cmp(self.start, other.start)
473                .then(float_cmp(self.step, other.step))
474                .then_with(|| match (self.end, other.end) {
475                    (Bound::Included(l), Bound::Included(r))
476                    | (Bound::Excluded(l), Bound::Excluded(r)) => {
477                        let ord = float_cmp(l, r);
478                        if self.step < 0.0 { ord.reverse() } else { ord }
479                    }
480                    (Bound::Included(l), Bound::Excluded(r)) => match float_cmp(l, r) {
481                        Ordering::Equal => Ordering::Greater,
482                        ord if self.step < 0.0 => ord.reverse(),
483                        ord => ord,
484                    },
485                    (Bound::Excluded(l), Bound::Included(r)) => match float_cmp(l, r) {
486                        Ordering::Equal => Ordering::Less,
487                        ord if self.step < 0.0 => ord.reverse(),
488                        ord => ord,
489                    },
490                    (Bound::Included(_), Bound::Unbounded) => Ordering::Less,
491                    (Bound::Excluded(_), Bound::Unbounded) => Ordering::Less,
492                    (Bound::Unbounded, Bound::Included(_)) => Ordering::Greater,
493                    (Bound::Unbounded, Bound::Excluded(_)) => Ordering::Greater,
494                    (Bound::Unbounded, Bound::Unbounded) => Ordering::Equal,
495                })
496        }
497    }
498
499    impl PartialOrd for FloatRange {
500        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
501            Some(self.cmp(other))
502        }
503    }
504
505    impl PartialEq for FloatRange {
506        fn eq(&self, other: &Self) -> bool {
507            self.start == other.start && self.step == other.step && self.end == other.end
508        }
509    }
510
511    impl Eq for FloatRange {}
512
513    impl Display for FloatRange {
514        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
515            write!(f, "{}..", ObviousFloat(self.start))?;
516            if self.step != 1f64 {
517                write!(f, "{}..", ObviousFloat(self.start + self.step))?;
518            }
519            match self.end {
520                Bound::Included(end) => write!(f, "{}", ObviousFloat(end)),
521                Bound::Excluded(end) => write!(f, "<{}", ObviousFloat(end)),
522                Bound::Unbounded => Ok(()),
523            }
524        }
525    }
526
527    impl From<IntRange> for FloatRange {
528        fn from(range: IntRange) -> Self {
529            Self {
530                start: range.start as f64,
531                step: range.step as f64,
532                end: match range.end {
533                    Bound::Included(b) => Bound::Included(b as f64),
534                    Bound::Excluded(b) => Bound::Excluded(b as f64),
535                    Bound::Unbounded => Bound::Unbounded,
536                },
537            }
538        }
539    }
540
541    impl From<Range> for FloatRange {
542        fn from(range: Range) -> Self {
543            match range {
544                Range::IntRange(range) => range.into(),
545                Range::FloatRange(range) => range,
546            }
547        }
548    }
549
550    pub struct Iter {
551        start: f64,
552        step: f64,
553        end: Bound<f64>,
554        iter: Option<u64>,
555        signals: Signals,
556    }
557
558    impl Iterator for Iter {
559        type Item = f64;
560
561        fn next(&mut self) -> Option<Self::Item> {
562            if let Some(iter) = self.iter {
563                let current = self.start + self.step * iter as f64;
564
565                let not_end = match (self.step < 0.0, self.end) {
566                    (true, Bound::Included(end)) => current >= end,
567                    (true, Bound::Excluded(end)) => current > end,
568                    (false, Bound::Included(end)) => current <= end,
569                    (false, Bound::Excluded(end)) => current < end,
570                    (_, Bound::Unbounded) => current.is_finite(),
571                };
572
573                if not_end && !self.signals.interrupted() {
574                    self.iter = iter.checked_add(1);
575                    Some(current)
576                } else {
577                    self.iter = None;
578                    None
579                }
580            } else {
581                None
582            }
583        }
584    }
585}
586
587pub use float_range::FloatRange;
588pub use int_range::IntRange;
589
590#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
591pub enum Range {
592    IntRange(IntRange),
593    FloatRange(FloatRange),
594}
595
596impl Range {
597    pub fn new(
598        start: Value,
599        next: Value,
600        end: Value,
601        inclusion: RangeInclusion,
602        span: Span,
603    ) -> Result<Self, ShellError> {
604        // promote to float range if any Value is float
605        if matches!(start, Value::Float { .. })
606            || matches!(next, Value::Float { .. })
607            || matches!(end, Value::Float { .. })
608        {
609            FloatRange::new(start, next, end, inclusion, span).map(Self::FloatRange)
610        } else {
611            IntRange::new(start, next, end, inclusion, span).map(Self::IntRange)
612        }
613    }
614
615    pub fn contains(&self, value: &Value) -> bool {
616        match (self, value) {
617            (Self::IntRange(range), Value::Int { val, .. }) => range.contains(*val),
618            (Self::IntRange(range), Value::Float { val, .. }) => {
619                FloatRange::from(*range).contains(*val)
620            }
621            (Self::FloatRange(range), Value::Int { val, .. }) => range.contains(*val as f64),
622            (Self::FloatRange(range), Value::Float { val, .. }) => range.contains(*val),
623            _ => false,
624        }
625    }
626
627    pub fn is_bounded(&self) -> bool {
628        match self {
629            Range::IntRange(range) => range.end() != Bound::<i64>::Unbounded,
630            Range::FloatRange(range) => range.end() != Bound::<f64>::Unbounded,
631        }
632    }
633
634    pub fn into_range_iter(self, span: Span, signals: Signals) -> Iter {
635        match self {
636            Range::IntRange(range) => Iter::IntIter(range.into_range_iter(signals), span),
637            Range::FloatRange(range) => Iter::FloatIter(range.into_range_iter(signals), span),
638        }
639    }
640}
641
642impl Ord for Range {
643    fn cmp(&self, other: &Self) -> Ordering {
644        match (self, other) {
645            (Range::IntRange(l), Range::IntRange(r)) => l.cmp(r),
646            (Range::FloatRange(l), Range::FloatRange(r)) => l.cmp(r),
647            (Range::IntRange(int), Range::FloatRange(float)) => FloatRange::from(*int).cmp(float),
648            (Range::FloatRange(float), Range::IntRange(int)) => float.cmp(&FloatRange::from(*int)),
649        }
650    }
651}
652
653impl PartialOrd for Range {
654    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
655        Some(self.cmp(other))
656    }
657}
658
659impl PartialEq for Range {
660    fn eq(&self, other: &Self) -> bool {
661        match (self, other) {
662            (Range::IntRange(l), Range::IntRange(r)) => l == r,
663            (Range::FloatRange(l), Range::FloatRange(r)) => l == r,
664            (Range::IntRange(int), Range::FloatRange(float)) => FloatRange::from(*int) == *float,
665            (Range::FloatRange(float), Range::IntRange(int)) => *float == FloatRange::from(*int),
666        }
667    }
668}
669
670impl Eq for Range {}
671
672impl Display for Range {
673    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
674        match self {
675            Range::IntRange(range) => write!(f, "{range}"),
676            Range::FloatRange(range) => write!(f, "{range}"),
677        }
678    }
679}
680
681impl From<IntRange> for Range {
682    fn from(range: IntRange) -> Self {
683        Self::IntRange(range)
684    }
685}
686
687impl From<FloatRange> for Range {
688    fn from(range: FloatRange) -> Self {
689        Self::FloatRange(range)
690    }
691}
692
693pub enum Iter {
694    IntIter(int_range::Iter, Span),
695    FloatIter(float_range::Iter, Span),
696}
697
698impl Iterator for Iter {
699    type Item = Value;
700
701    fn next(&mut self) -> Option<Self::Item> {
702        match self {
703            Iter::IntIter(iter, span) => iter.next().map(|val| Value::int(val, *span)),
704            Iter::FloatIter(iter, span) => iter.next().map(|val| Value::float(val, *span)),
705        }
706    }
707}