1use 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 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 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 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 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 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, };
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 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 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 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 a.partial_cmp(&b).expect("not NaN")
463 }
464
465 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 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}