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::{
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 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 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 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 a.partial_cmp(&b).expect("not NaN")
465 }
466
467 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 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}