nu_protocol/value/
range.rs

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