1use crate::{ast::RangeInclusion, ShellError, Signals, Span, Value};
4use core::ops::Bound;
5use serde::{Deserialize, Serialize};
6use std::{cmp::Ordering, fmt::Display};
7
8mod int_range {
9 use crate::{ast::RangeInclusion, FromValue, ShellError, Signals, Span, Value};
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 {
213 ord.reverse()
214 } else {
215 ord
216 }
217 }
218 (Bound::Included(l), Bound::Excluded(r)) => match l.cmp(&r) {
219 Ordering::Equal => Ordering::Greater,
220 ord if self.step < 0 => ord.reverse(),
221 ord => ord,
222 },
223 (Bound::Excluded(l), Bound::Included(r)) => match l.cmp(&r) {
224 Ordering::Equal => Ordering::Less,
225 ord if self.step < 0 => ord.reverse(),
226 ord => ord,
227 },
228 (Bound::Included(_), Bound::Unbounded) => Ordering::Less,
229 (Bound::Excluded(_), Bound::Unbounded) => Ordering::Less,
230 (Bound::Unbounded, Bound::Included(_)) => Ordering::Greater,
231 (Bound::Unbounded, Bound::Excluded(_)) => Ordering::Greater,
232 (Bound::Unbounded, Bound::Unbounded) => Ordering::Equal,
233 })
234 }
235 }
236
237 impl PartialOrd for IntRange {
238 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
239 Some(self.cmp(other))
240 }
241 }
242
243 impl PartialEq for IntRange {
244 fn eq(&self, other: &Self) -> bool {
245 self.start == other.start && self.step == other.step && self.end == other.end
246 }
247 }
248
249 impl Eq for IntRange {}
250
251 impl Display for IntRange {
252 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253 write!(f, "{}..", self.start)?;
254 if self.step != 1 {
255 write!(f, "{}..", self.start + self.step)?;
256 }
257 match self.end {
258 Bound::Included(end) => write!(f, "{end}"),
259 Bound::Excluded(end) => write!(f, "<{end}"),
260 Bound::Unbounded => Ok(()),
261 }
262 }
263 }
264
265 impl FromValue for IntRange {
266 fn from_value(v: Value) -> Result<Self, ShellError> {
267 let span = v.span();
268 let range = Range::from_value(v)?;
269 match range {
270 Range::IntRange(v) => Ok(v),
271 Range::FloatRange(_) => Err(ShellError::TypeMismatch {
272 err_message: "expected an int range".into(),
273 span,
274 }),
275 }
276 }
277 }
278
279 pub struct Iter {
280 current: Option<i64>,
281 step: i64,
282 end: Bound<i64>,
283 signals: Signals,
284 }
285
286 impl Iterator for Iter {
287 type Item = i64;
288
289 fn next(&mut self) -> Option<Self::Item> {
290 if let Some(current) = self.current {
291 let not_end = match (self.step < 0, self.end) {
292 (true, Bound::Included(end)) => current >= end,
293 (true, Bound::Excluded(end)) => current > end,
294 (false, Bound::Included(end)) => current <= end,
295 (false, Bound::Excluded(end)) => current < end,
296 (_, Bound::Unbounded) => true, };
298
299 if not_end && !self.signals.interrupted() {
300 self.current = current.checked_add(self.step);
301 Some(current)
302 } else {
303 self.current = None;
304 None
305 }
306 } else {
307 None
308 }
309 }
310 }
311}
312
313mod float_range {
314 use crate::{
315 ast::RangeInclusion, format::ObviousFloat, IntRange, Range, ShellError, Signals, Span,
316 Value,
317 };
318 use serde::{Deserialize, Serialize};
319 use std::{cmp::Ordering, fmt::Display, ops::Bound};
320
321 #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
322 pub struct FloatRange {
323 pub(crate) start: f64,
324 pub(crate) step: f64,
325 pub(crate) end: Bound<f64>,
326 }
327
328 impl FloatRange {
329 pub fn new(
330 start: Value,
331 next: Value,
332 end: Value,
333 inclusion: RangeInclusion,
334 span: Span,
335 ) -> Result<Self, ShellError> {
336 fn to_float(value: Value) -> Result<Option<f64>, ShellError> {
337 match value {
338 Value::Float { val, .. } => Ok(Some(val)),
339 Value::Int { val, .. } => Ok(Some(val as f64)),
340 Value::Nothing { .. } => Ok(None),
341 val => Err(ShellError::CantConvert {
342 to_type: "float".into(),
343 from_type: val.get_type().to_string(),
344 span: val.span(),
345 help: None,
346 }),
347 }
348 }
349
350 let start_span = start.span();
357 let start = to_float(start)?.unwrap_or(0.0);
358 if !start.is_finite() {
359 return Err(ShellError::CannotCreateRange { span: start_span });
360 }
361
362 let end_span = end.span();
363 let end = to_float(end)?;
364 if end.is_some_and(f64::is_nan) {
365 return Err(ShellError::CannotCreateRange { span: end_span });
366 }
367
368 let next_span = next.span();
369 let next = to_float(next)?;
370 if next.is_some_and(|next| next == start || !next.is_finite()) {
371 return Err(ShellError::CannotCreateRange { span: next_span });
372 }
373
374 let step = match (next, end) {
375 (Some(next), Some(end)) => {
376 if (next < start) != (end < start) {
377 return Err(ShellError::CannotCreateRange { span });
378 }
379 next - start
380 }
381 (Some(next), None) => next - start,
382 (None, Some(end)) => {
383 if end < start {
384 -1.0
385 } else {
386 1.0
387 }
388 }
389 (None, None) => 1.0,
390 };
391
392 let end = if let Some(end) = end {
393 if end.is_infinite() {
394 Bound::Unbounded
395 } else {
396 match inclusion {
397 RangeInclusion::Inclusive => Bound::Included(end),
398 RangeInclusion::RightExclusive => Bound::Excluded(end),
399 }
400 }
401 } else {
402 Bound::Unbounded
403 };
404
405 Ok(Self { start, step, end })
406 }
407
408 pub fn start(&self) -> f64 {
409 self.start
410 }
411
412 pub fn end(&self) -> Bound<f64> {
413 self.end
414 }
415
416 pub fn step(&self) -> f64 {
417 self.step
418 }
419
420 pub fn is_unbounded(&self) -> bool {
421 self.end == Bound::Unbounded
422 }
423
424 pub fn contains(&self, value: f64) -> bool {
425 if self.step < 0.0 {
426 if value > self.start {
428 return false;
429 }
430 match self.end {
431 Bound::Included(end) if value <= end => return false,
432 Bound::Excluded(end) if value < end => return false,
433 _ => {}
434 };
435 } else {
436 if value < self.start {
438 return false;
439 }
440 match self.end {
441 Bound::Included(end) if value >= end => return false,
442 Bound::Excluded(end) if value > end => return false,
443 _ => {}
444 };
445 }
446 ((value - self.start) % self.step).abs() < f64::EPSILON
447 }
448
449 pub fn into_range_iter(self, signals: Signals) -> Iter {
450 Iter {
451 start: self.start,
452 step: self.step,
453 end: self.end,
454 iter: Some(0),
455 signals,
456 }
457 }
458 }
459
460 impl Ord for FloatRange {
461 fn cmp(&self, other: &Self) -> Ordering {
462 fn float_cmp(a: f64, b: f64) -> Ordering {
463 a.partial_cmp(&b).expect("not NaN")
469 }
470
471 float_cmp(self.start, other.start)
477 .then(float_cmp(self.step, other.step))
478 .then_with(|| match (self.end, other.end) {
479 (Bound::Included(l), Bound::Included(r))
480 | (Bound::Excluded(l), Bound::Excluded(r)) => {
481 let ord = float_cmp(l, r);
482 if self.step < 0.0 {
483 ord.reverse()
484 } else {
485 ord
486 }
487 }
488 (Bound::Included(l), Bound::Excluded(r)) => match float_cmp(l, r) {
489 Ordering::Equal => Ordering::Greater,
490 ord if self.step < 0.0 => ord.reverse(),
491 ord => ord,
492 },
493 (Bound::Excluded(l), Bound::Included(r)) => match float_cmp(l, r) {
494 Ordering::Equal => Ordering::Less,
495 ord if self.step < 0.0 => ord.reverse(),
496 ord => ord,
497 },
498 (Bound::Included(_), Bound::Unbounded) => Ordering::Less,
499 (Bound::Excluded(_), Bound::Unbounded) => Ordering::Less,
500 (Bound::Unbounded, Bound::Included(_)) => Ordering::Greater,
501 (Bound::Unbounded, Bound::Excluded(_)) => Ordering::Greater,
502 (Bound::Unbounded, Bound::Unbounded) => Ordering::Equal,
503 })
504 }
505 }
506
507 impl PartialOrd for FloatRange {
508 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
509 Some(self.cmp(other))
510 }
511 }
512
513 impl PartialEq for FloatRange {
514 fn eq(&self, other: &Self) -> bool {
515 self.start == other.start && self.step == other.step && self.end == other.end
516 }
517 }
518
519 impl Eq for FloatRange {}
520
521 impl Display for FloatRange {
522 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
523 write!(f, "{}..", ObviousFloat(self.start))?;
524 if self.step != 1f64 {
525 write!(f, "{}..", ObviousFloat(self.start + self.step))?;
526 }
527 match self.end {
528 Bound::Included(end) => write!(f, "{}", ObviousFloat(end)),
529 Bound::Excluded(end) => write!(f, "<{}", ObviousFloat(end)),
530 Bound::Unbounded => Ok(()),
531 }
532 }
533 }
534
535 impl From<IntRange> for FloatRange {
536 fn from(range: IntRange) -> Self {
537 Self {
538 start: range.start as f64,
539 step: range.step as f64,
540 end: match range.end {
541 Bound::Included(b) => Bound::Included(b as f64),
542 Bound::Excluded(b) => Bound::Excluded(b as f64),
543 Bound::Unbounded => Bound::Unbounded,
544 },
545 }
546 }
547 }
548
549 impl From<Range> for FloatRange {
550 fn from(range: Range) -> Self {
551 match range {
552 Range::IntRange(range) => range.into(),
553 Range::FloatRange(range) => range,
554 }
555 }
556 }
557
558 pub struct Iter {
559 start: f64,
560 step: f64,
561 end: Bound<f64>,
562 iter: Option<u64>,
563 signals: Signals,
564 }
565
566 impl Iterator for Iter {
567 type Item = f64;
568
569 fn next(&mut self) -> Option<Self::Item> {
570 if let Some(iter) = self.iter {
571 let current = self.start + self.step * iter as f64;
572
573 let not_end = match (self.step < 0.0, self.end) {
574 (true, Bound::Included(end)) => current >= end,
575 (true, Bound::Excluded(end)) => current > end,
576 (false, Bound::Included(end)) => current <= end,
577 (false, Bound::Excluded(end)) => current < end,
578 (_, Bound::Unbounded) => current.is_finite(),
579 };
580
581 if not_end && !self.signals.interrupted() {
582 self.iter = iter.checked_add(1);
583 Some(current)
584 } else {
585 self.iter = None;
586 None
587 }
588 } else {
589 None
590 }
591 }
592 }
593}
594
595pub use float_range::FloatRange;
596pub use int_range::IntRange;
597
598#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
599pub enum Range {
600 IntRange(IntRange),
601 FloatRange(FloatRange),
602}
603
604impl Range {
605 pub fn new(
606 start: Value,
607 next: Value,
608 end: Value,
609 inclusion: RangeInclusion,
610 span: Span,
611 ) -> Result<Self, ShellError> {
612 if matches!(start, Value::Float { .. })
614 || matches!(next, Value::Float { .. })
615 || matches!(end, Value::Float { .. })
616 {
617 FloatRange::new(start, next, end, inclusion, span).map(Self::FloatRange)
618 } else {
619 IntRange::new(start, next, end, inclusion, span).map(Self::IntRange)
620 }
621 }
622
623 pub fn contains(&self, value: &Value) -> bool {
624 match (self, value) {
625 (Self::IntRange(range), Value::Int { val, .. }) => range.contains(*val),
626 (Self::IntRange(range), Value::Float { val, .. }) => {
627 FloatRange::from(*range).contains(*val)
628 }
629 (Self::FloatRange(range), Value::Int { val, .. }) => range.contains(*val as f64),
630 (Self::FloatRange(range), Value::Float { val, .. }) => range.contains(*val),
631 _ => false,
632 }
633 }
634
635 pub fn is_bounded(&self) -> bool {
636 match self {
637 Range::IntRange(range) => range.end() != Bound::<i64>::Unbounded,
638 Range::FloatRange(range) => range.end() != Bound::<f64>::Unbounded,
639 }
640 }
641
642 pub fn into_range_iter(self, span: Span, signals: Signals) -> Iter {
643 match self {
644 Range::IntRange(range) => Iter::IntIter(range.into_range_iter(signals), span),
645 Range::FloatRange(range) => Iter::FloatIter(range.into_range_iter(signals), span),
646 }
647 }
648}
649
650impl Ord for Range {
651 fn cmp(&self, other: &Self) -> Ordering {
652 match (self, other) {
653 (Range::IntRange(l), Range::IntRange(r)) => l.cmp(r),
654 (Range::FloatRange(l), Range::FloatRange(r)) => l.cmp(r),
655 (Range::IntRange(int), Range::FloatRange(float)) => FloatRange::from(*int).cmp(float),
656 (Range::FloatRange(float), Range::IntRange(int)) => float.cmp(&FloatRange::from(*int)),
657 }
658 }
659}
660
661impl PartialOrd for Range {
662 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
663 Some(self.cmp(other))
664 }
665}
666
667impl PartialEq for Range {
668 fn eq(&self, other: &Self) -> bool {
669 match (self, other) {
670 (Range::IntRange(l), Range::IntRange(r)) => l == r,
671 (Range::FloatRange(l), Range::FloatRange(r)) => l == r,
672 (Range::IntRange(int), Range::FloatRange(float)) => FloatRange::from(*int) == *float,
673 (Range::FloatRange(float), Range::IntRange(int)) => *float == FloatRange::from(*int),
674 }
675 }
676}
677
678impl Eq for Range {}
679
680impl Display for Range {
681 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
682 match self {
683 Range::IntRange(range) => write!(f, "{range}"),
684 Range::FloatRange(range) => write!(f, "{range}"),
685 }
686 }
687}
688
689impl From<IntRange> for Range {
690 fn from(range: IntRange) -> Self {
691 Self::IntRange(range)
692 }
693}
694
695impl From<FloatRange> for Range {
696 fn from(range: FloatRange) -> Self {
697 Self::FloatRange(range)
698 }
699}
700
701pub enum Iter {
702 IntIter(int_range::Iter, Span),
703 FloatIter(float_range::Iter, Span),
704}
705
706impl Iterator for Iter {
707 type Item = Value;
708
709 fn next(&mut self) -> Option<Self::Item> {
710 match self {
711 Iter::IntIter(iter, span) => iter.next().map(|val| Value::int(val, *span)),
712 Iter::FloatIter(iter, span) => iter.next().map(|val| Value::float(val, *span)),
713 }
714 }
715}