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