1use crate::{ShellError, Signals, Span, Value, ast::RangeInclusion};
4use core::ops::Bound;
5use serde::{Deserialize, Serialize};
6use std::{cmp::Ordering, fmt::Display, str::FromStr};
7use winnow::Parser;
8
9mod int_range {
10 use crate::{FromValue, ShellError, Signals, Span, Value, ast::RangeInclusion};
11 use serde::{Deserialize, Serialize};
12 use std::{cmp::Ordering, fmt::Display, ops::Bound};
13
14 use super::Range;
15
16 #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
17 pub struct IntRange {
18 pub(crate) start: i64,
19 pub(crate) step: i64,
20 pub(crate) end: Bound<i64>,
21 }
22
23 impl IntRange {
24 pub fn new(
25 start: Value,
26 next: Value,
27 end: Value,
28 inclusion: RangeInclusion,
29 span: Span,
30 ) -> Result<Self, ShellError> {
31 fn to_int(value: Value) -> Result<Option<i64>, ShellError> {
32 match value {
33 Value::Int { val, .. } => Ok(Some(val)),
34 Value::Nothing { .. } => Ok(None),
35 val => Err(ShellError::CantConvert {
36 to_type: "int".into(),
37 from_type: val.get_type().to_string(),
38 span: val.span(),
39 help: None,
40 }),
41 }
42 }
43
44 let start = to_int(start)?.unwrap_or(0);
45
46 let next_span = next.span();
47 let next = to_int(next)?;
48 if next.is_some_and(|next| next == start) {
49 return Err(ShellError::CannotCreateRange { span: next_span });
50 }
51
52 let end = to_int(end)?;
53
54 let step = match (next, end) {
55 (Some(next), Some(end)) => {
56 if (next < start) != (end < start) {
57 return Err(ShellError::CannotCreateRange { span });
58 }
59 next - start
60 }
61 (Some(next), None) => next - start,
62 (None, Some(end)) => {
63 if end < start {
64 -1
65 } else {
66 1
67 }
68 }
69 (None, None) => 1,
70 };
71
72 let end = if let Some(end) = end {
73 match inclusion {
74 RangeInclusion::Inclusive => Bound::Included(end),
75 RangeInclusion::RightExclusive => Bound::Excluded(end),
76 }
77 } else {
78 Bound::Unbounded
79 };
80
81 Ok(Self { start, step, end })
82 }
83
84 pub fn start(&self) -> i64 {
85 self.start
86 }
87
88 pub fn absolute_start(&self, len: u64) -> u64 {
90 match self.start {
91 start if start < 0 => len.saturating_sub(start.unsigned_abs()),
92 start => len.min(start.unsigned_abs()),
93 }
94 }
95
96 pub fn distance(&self) -> Bound<u64> {
99 match self.end {
100 Bound::Unbounded => Bound::Unbounded,
101 Bound::Included(end) | Bound::Excluded(end) if self.start > end => {
102 Bound::Excluded(0)
103 }
104 Bound::Included(end) => Bound::Included((end - self.start) as u64),
105 Bound::Excluded(end) => Bound::Excluded((end - self.start) as u64),
106 }
107 }
108
109 pub fn end(&self) -> Bound<i64> {
110 self.end
111 }
112
113 pub fn absolute_end(&self, len: u64) -> Bound<u64> {
114 match self.end {
115 Bound::Unbounded => Bound::Unbounded,
116 Bound::Included(i) => match i {
117 _ if len == 0 => Bound::Excluded(0),
118 i if i < 0 => Bound::Excluded(len.saturating_sub((i + 1).unsigned_abs())),
119 i => Bound::Included((len.saturating_sub(1)).min(i.unsigned_abs())),
120 },
121 Bound::Excluded(i) => Bound::Excluded(match i {
122 i if i < 0 => len.saturating_sub(i.unsigned_abs()),
123 i => len.min(i.unsigned_abs()),
124 }),
125 }
126 }
127
128 pub fn absolute_bounds(&self, len: usize) -> (usize, Bound<usize>) {
129 let start = self.absolute_start(len as u64) as usize;
130 let end = self.absolute_end(len as u64).map(|e| e as usize);
131 match end {
132 Bound::Excluded(end) | Bound::Included(end) if end < start => {
133 (start, Bound::Excluded(start))
134 }
135 Bound::Excluded(end) => (start, Bound::Excluded(end)),
136 Bound::Included(end) => (start, Bound::Included(end)),
137 Bound::Unbounded => (start, Bound::Unbounded),
138 }
139 }
140
141 pub fn step(&self) -> i64 {
142 self.step
143 }
144
145 pub fn is_unbounded(&self) -> bool {
146 self.end == Bound::Unbounded
147 }
148
149 pub fn is_relative(&self) -> bool {
150 self.is_start_relative() || self.is_end_relative()
151 }
152
153 pub fn is_start_relative(&self) -> bool {
154 self.start < 0
155 }
156
157 pub fn is_end_relative(&self) -> bool {
158 match self.end {
159 Bound::Included(end) | Bound::Excluded(end) => end < 0,
160 _ => false,
161 }
162 }
163
164 pub fn contains(&self, value: i64) -> bool {
165 if self.step < 0 {
166 if value > self.start {
168 return false;
169 }
170 match self.end {
171 Bound::Included(end) if value < end => return false,
172 Bound::Excluded(end) if value <= end => return false,
173 _ => {}
174 };
175 } else {
176 if value < self.start {
178 return false;
179 }
180 match self.end {
181 Bound::Included(end) if value > end => return false,
182 Bound::Excluded(end) if value >= end => return false,
183 _ => {}
184 };
185 }
186 (value - self.start) % self.step == 0
187 }
188
189 pub fn into_range_iter(self, signals: Signals) -> Iter {
190 Iter {
191 current: Some(self.start),
192 step: self.step,
193 end: self.end,
194 signals,
195 }
196 }
197 }
198
199 impl Ord for IntRange {
200 fn cmp(&self, other: &Self) -> Ordering {
201 self.start
207 .cmp(&other.start)
208 .then(self.step.cmp(&other.step))
209 .then_with(|| match (self.end, other.end) {
210 (Bound::Included(l), Bound::Included(r))
211 | (Bound::Excluded(l), Bound::Excluded(r)) => {
212 let ord = l.cmp(&r);
213 if self.step < 0 { ord.reverse() } else { ord }
214 }
215 (Bound::Included(l), Bound::Excluded(r)) => match l.cmp(&r) {
216 Ordering::Equal => Ordering::Greater,
217 ord if self.step < 0 => ord.reverse(),
218 ord => ord,
219 },
220 (Bound::Excluded(l), Bound::Included(r)) => match l.cmp(&r) {
221 Ordering::Equal => Ordering::Less,
222 ord if self.step < 0 => ord.reverse(),
223 ord => ord,
224 },
225 (Bound::Included(_), Bound::Unbounded) => Ordering::Less,
226 (Bound::Excluded(_), Bound::Unbounded) => Ordering::Less,
227 (Bound::Unbounded, Bound::Included(_)) => Ordering::Greater,
228 (Bound::Unbounded, Bound::Excluded(_)) => Ordering::Greater,
229 (Bound::Unbounded, Bound::Unbounded) => Ordering::Equal,
230 })
231 }
232 }
233
234 impl PartialOrd for IntRange {
235 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
236 Some(self.cmp(other))
237 }
238 }
239
240 impl PartialEq for IntRange {
241 fn eq(&self, other: &Self) -> bool {
242 self.start == other.start && self.step == other.step && self.end == other.end
243 }
244 }
245
246 impl Eq for IntRange {}
247
248 impl Display for IntRange {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 write!(f, "{}..", self.start)?;
251 if self.step != 1 {
252 write!(f, "{}..", self.start + self.step)?;
253 }
254 match self.end {
255 Bound::Included(end) => write!(f, "{end}"),
256 Bound::Excluded(end) => write!(f, "<{end}"),
257 Bound::Unbounded => Ok(()),
258 }
259 }
260 }
261
262 impl FromValue for IntRange {
263 fn from_value(v: Value) -> Result<Self, ShellError> {
264 let span = v.span();
265 let range = Range::from_value(v)?;
266 match range {
267 Range::IntRange(v) => Ok(v),
268 Range::FloatRange(_) => Err(ShellError::TypeMismatch {
269 err_message: "expected an int range".into(),
270 span,
271 }),
272 }
273 }
274 }
275
276 pub struct Iter {
277 current: Option<i64>,
278 step: i64,
279 end: Bound<i64>,
280 signals: Signals,
281 }
282
283 impl Iterator for Iter {
284 type Item = i64;
285
286 fn next(&mut self) -> Option<Self::Item> {
287 if let Some(current) = self.current {
288 let not_end = match (self.step < 0, self.end) {
289 (true, Bound::Included(end)) => current >= end,
290 (true, Bound::Excluded(end)) => current > end,
291 (false, Bound::Included(end)) => current <= end,
292 (false, Bound::Excluded(end)) => current < end,
293 (_, Bound::Unbounded) => true, };
295
296 if not_end && !self.signals.interrupted() {
297 self.current = current.checked_add(self.step);
298 Some(current)
299 } else {
300 self.current = None;
301 None
302 }
303 } else {
304 None
305 }
306 }
307 }
308}
309
310mod float_range {
311 use crate::{IntRange, Range, ShellError, Signals, Span, Value, ast::RangeInclusion};
312 use nu_utils::ObviousFloat;
313 use serde::{Deserialize, Serialize};
314 use std::{cmp::Ordering, fmt::Display, ops::Bound};
315
316 #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
317 pub struct FloatRange {
318 pub(crate) start: f64,
319 pub(crate) step: f64,
320 pub(crate) end: Bound<f64>,
321 }
322
323 impl FloatRange {
324 pub fn new(
325 start: Value,
326 next: Value,
327 end: Value,
328 inclusion: RangeInclusion,
329 span: Span,
330 ) -> Result<Self, ShellError> {
331 fn to_float(value: Value) -> Result<Option<f64>, ShellError> {
332 match value {
333 Value::Float { val, .. } => Ok(Some(val)),
334 Value::Int { val, .. } => Ok(Some(val as f64)),
335 Value::Nothing { .. } => Ok(None),
336 val => Err(ShellError::CantConvert {
337 to_type: "float".into(),
338 from_type: val.get_type().to_string(),
339 span: val.span(),
340 help: None,
341 }),
342 }
343 }
344
345 let start_span = start.span();
352 let start = to_float(start)?.unwrap_or(0.0);
353 if !start.is_finite() {
354 return Err(ShellError::CannotCreateRange { span: start_span });
355 }
356
357 let end_span = end.span();
358 let end = to_float(end)?;
359 if end.is_some_and(f64::is_nan) {
360 return Err(ShellError::CannotCreateRange { span: end_span });
361 }
362
363 let next_span = next.span();
364 let next = to_float(next)?;
365 if next.is_some_and(|next| next == start || !next.is_finite()) {
366 return Err(ShellError::CannotCreateRange { span: next_span });
367 }
368
369 let step = match (next, end) {
370 (Some(next), Some(end)) => {
371 if (next < start) != (end < start) {
372 return Err(ShellError::CannotCreateRange { span });
373 }
374 next - start
375 }
376 (Some(next), None) => next - start,
377 (None, Some(end)) => {
378 let diff = end - start;
379 if diff == 0.0 {
380 return Err(ShellError::CannotCreateRange { span });
381 }
382 if diff.abs() < 1.0 {
383 let magnitude = 10.0_f64.powf(diff.abs().log10().floor());
387 diff.signum() * magnitude
388 } else if diff > 0.0 {
389 1.0
390 } else {
391 -1.0
392 }
393 }
394 (None, None) => 1.0,
395 };
396
397 let end = if let Some(end) = end {
398 if end.is_infinite() {
399 Bound::Unbounded
400 } else {
401 match inclusion {
402 RangeInclusion::Inclusive => Bound::Included(end),
403 RangeInclusion::RightExclusive => Bound::Excluded(end),
404 }
405 }
406 } else {
407 Bound::Unbounded
408 };
409
410 Ok(Self { start, step, end })
411 }
412
413 pub fn start(&self) -> f64 {
414 self.start
415 }
416
417 pub fn end(&self) -> Bound<f64> {
418 self.end
419 }
420
421 pub fn step(&self) -> f64 {
422 self.step
423 }
424
425 pub fn is_unbounded(&self) -> bool {
426 self.end == Bound::Unbounded
427 }
428
429 pub fn contains(&self, value: f64) -> bool {
430 if self.step < 0.0 {
431 if value > self.start {
433 return false;
434 }
435 match self.end {
436 Bound::Included(end) if value <= end => return false,
437 Bound::Excluded(end) if value < end => return false,
438 _ => {}
439 };
440 } else {
441 if value < self.start {
443 return false;
444 }
445 match self.end {
446 Bound::Included(end) if value >= end => return false,
447 Bound::Excluded(end) if value > end => return false,
448 _ => {}
449 };
450 }
451 ((value - self.start) % self.step).abs() < f64::EPSILON
452 }
453
454 pub fn into_range_iter(self, signals: Signals) -> Iter {
455 let round_factor = if self.step.abs() >= 1.0 || self.step == 0.0 {
459 0.0 } else {
461 let precision = (-self.step.abs().log10()).max(0.0).ceil() as i32;
462 10.0_f64.powi(precision)
463 };
464 Iter {
465 start: self.start,
466 step: self.step,
467 end: self.end,
468 iter: Some(0),
469 round_factor,
470 signals,
471 }
472 }
473 }
474
475 impl Ord for FloatRange {
476 fn cmp(&self, other: &Self) -> Ordering {
477 fn float_cmp(a: f64, b: f64) -> Ordering {
478 a.partial_cmp(&b).expect("not NaN")
484 }
485
486 float_cmp(self.start, other.start)
492 .then(float_cmp(self.step, other.step))
493 .then_with(|| match (self.end, other.end) {
494 (Bound::Included(l), Bound::Included(r))
495 | (Bound::Excluded(l), Bound::Excluded(r)) => {
496 let ord = float_cmp(l, r);
497 if self.step < 0.0 { ord.reverse() } else { ord }
498 }
499 (Bound::Included(l), Bound::Excluded(r)) => match float_cmp(l, r) {
500 Ordering::Equal => Ordering::Greater,
501 ord if self.step < 0.0 => ord.reverse(),
502 ord => ord,
503 },
504 (Bound::Excluded(l), Bound::Included(r)) => match float_cmp(l, r) {
505 Ordering::Equal => Ordering::Less,
506 ord if self.step < 0.0 => ord.reverse(),
507 ord => ord,
508 },
509 (Bound::Included(_), Bound::Unbounded) => Ordering::Less,
510 (Bound::Excluded(_), Bound::Unbounded) => Ordering::Less,
511 (Bound::Unbounded, Bound::Included(_)) => Ordering::Greater,
512 (Bound::Unbounded, Bound::Excluded(_)) => Ordering::Greater,
513 (Bound::Unbounded, Bound::Unbounded) => Ordering::Equal,
514 })
515 }
516 }
517
518 impl PartialOrd for FloatRange {
519 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
520 Some(self.cmp(other))
521 }
522 }
523
524 impl PartialEq for FloatRange {
525 fn eq(&self, other: &Self) -> bool {
526 self.start == other.start && self.step == other.step && self.end == other.end
527 }
528 }
529
530 impl Eq for FloatRange {}
531
532 impl Display for FloatRange {
533 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
534 write!(f, "{}..", ObviousFloat(self.start))?;
535 if self.step != 1f64 {
536 write!(f, "{}..", ObviousFloat(self.start + self.step))?;
537 }
538 match self.end {
539 Bound::Included(end) => write!(f, "{}", ObviousFloat(end)),
540 Bound::Excluded(end) => write!(f, "<{}", ObviousFloat(end)),
541 Bound::Unbounded => Ok(()),
542 }
543 }
544 }
545
546 impl From<IntRange> for FloatRange {
547 fn from(range: IntRange) -> Self {
548 Self {
549 start: range.start as f64,
550 step: range.step as f64,
551 end: match range.end {
552 Bound::Included(b) => Bound::Included(b as f64),
553 Bound::Excluded(b) => Bound::Excluded(b as f64),
554 Bound::Unbounded => Bound::Unbounded,
555 },
556 }
557 }
558 }
559
560 impl From<Range> for FloatRange {
561 fn from(range: Range) -> Self {
562 match range {
563 Range::IntRange(range) => range.into(),
564 Range::FloatRange(range) => range,
565 }
566 }
567 }
568
569 pub struct Iter {
570 start: f64,
571 step: f64,
572 end: Bound<f64>,
573 iter: Option<u64>,
574 round_factor: f64,
575 signals: Signals,
576 }
577
578 impl Iterator for Iter {
579 type Item = f64;
580
581 fn next(&mut self) -> Option<Self::Item> {
582 if let Some(iter) = self.iter {
583 let current = self.start + self.step * iter as f64;
584
585 let quotient = current / self.step;
587 let value = if (quotient - quotient.round()).abs() < 1e-10 {
588 quotient.round() * self.step
589 } else {
590 current
591 };
592
593 let value = if self.round_factor > 0.0 {
596 (value * self.round_factor).round() / self.round_factor
597 } else {
598 value
599 };
600
601 const EPS: f64 = f64::EPSILON * 100.0;
604 let not_end = match (self.step < 0.0, self.end) {
605 (true, Bound::Included(end)) => value + EPS >= end,
606 (true, Bound::Excluded(end)) => value - EPS > end,
607 (false, Bound::Included(end)) => value <= end + EPS,
608 (false, Bound::Excluded(end)) => value < end - EPS,
609 (_, Bound::Unbounded) => value.is_finite(),
610 };
611
612 if not_end && !self.signals.interrupted() {
613 self.iter = iter.checked_add(1);
614 Some(value)
615 } else {
616 self.iter = None;
617 None
618 }
619 } else {
620 None
621 }
622 }
623 }
624}
625
626pub use float_range::FloatRange;
627pub use int_range::IntRange;
628
629#[derive(Debug, Clone, Copy)]
630pub enum Range {
631 IntRange(IntRange),
632 FloatRange(FloatRange),
633}
634
635impl Range {
636 pub fn new(
637 start: Value,
638 next: Value,
639 end: Value,
640 inclusion: RangeInclusion,
641 span: Span,
642 ) -> Result<Self, ShellError> {
643 if matches!(start, Value::Float { .. })
645 || matches!(next, Value::Float { .. })
646 || matches!(end, Value::Float { .. })
647 {
648 FloatRange::new(start, next, end, inclusion, span).map(Self::FloatRange)
649 } else {
650 IntRange::new(start, next, end, inclusion, span).map(Self::IntRange)
651 }
652 }
653
654 pub fn new_int(
655 start: impl Into<Option<i64>>,
656 next: impl Into<Option<i64>>,
657 end: impl Into<Option<Bound<i64>>>,
658 ) -> Self {
659 let start = start.into().unwrap_or(0);
660 let end = end.into().unwrap_or(Bound::Unbounded);
661 let step = next.into().map(|next| next - start).unwrap_or(match end {
662 Bound::Unbounded => 1,
663 Bound::Included(end) | Bound::Excluded(end) if start <= end => 1,
664 _ => -1,
665 });
666 Range::IntRange(IntRange { start, step, end })
667 }
668
669 pub fn new_float(
670 start: impl Into<Option<f64>>,
671 next: impl Into<Option<f64>>,
672 end: impl Into<Option<Bound<f64>>>,
673 ) -> Self {
674 let start = start.into().unwrap_or(0.0);
675 let end = end.into().unwrap_or(Bound::Unbounded);
676 let step = next.into().map(|next| next - start).unwrap_or(match end {
677 Bound::Unbounded => 1.0,
678 Bound::Included(end) | Bound::Excluded(end) if start <= end => 1.0,
679 _ => -1.0,
680 });
681 Range::FloatRange(FloatRange { start, step, end })
682 }
683
684 pub fn contains(&self, value: &Value) -> bool {
685 match (self, value) {
686 (Self::IntRange(range), Value::Int { val, .. }) => range.contains(*val),
687 (Self::IntRange(range), Value::Float { val, .. }) => {
688 FloatRange::from(*range).contains(*val)
689 }
690 (Self::FloatRange(range), Value::Int { val, .. }) => range.contains(*val as f64),
691 (Self::FloatRange(range), Value::Float { val, .. }) => range.contains(*val),
692 _ => false,
693 }
694 }
695
696 pub fn is_bounded(&self) -> bool {
697 match self {
698 Range::IntRange(range) => range.end() != Bound::<i64>::Unbounded,
699 Range::FloatRange(range) => range.end() != Bound::<f64>::Unbounded,
700 }
701 }
702
703 pub fn into_range_iter(self, span: Span, signals: Signals) -> Iter {
704 match self {
705 Range::IntRange(range) => Iter::IntIter(range.into_range_iter(signals), span),
706 Range::FloatRange(range) => Iter::FloatIter(range.into_range_iter(signals), span),
707 }
708 }
709
710 pub fn memory_size(&self) -> usize {
712 std::mem::size_of::<Self>()
713 }
714}
715
716impl Ord for Range {
717 fn cmp(&self, other: &Self) -> Ordering {
718 match (self, other) {
719 (Range::IntRange(l), Range::IntRange(r)) => l.cmp(r),
720 (Range::FloatRange(l), Range::FloatRange(r)) => l.cmp(r),
721 (Range::IntRange(int), Range::FloatRange(float)) => FloatRange::from(*int).cmp(float),
722 (Range::FloatRange(float), Range::IntRange(int)) => float.cmp(&FloatRange::from(*int)),
723 }
724 }
725}
726
727impl PartialOrd for Range {
728 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
729 Some(self.cmp(other))
730 }
731}
732
733impl PartialEq for Range {
734 fn eq(&self, other: &Self) -> bool {
735 match (self, other) {
736 (Range::IntRange(l), Range::IntRange(r)) => l == r,
737 (Range::FloatRange(l), Range::FloatRange(r)) => l == r,
738 (Range::IntRange(int), Range::FloatRange(float)) => FloatRange::from(*int) == *float,
739 (Range::FloatRange(float), Range::IntRange(int)) => *float == FloatRange::from(*int),
740 }
741 }
742}
743
744impl Eq for Range {}
745
746impl Display for Range {
747 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
748 match self {
749 Range::IntRange(range) => write!(f, "{range}"),
750 Range::FloatRange(range) => write!(f, "{range}"),
751 }
752 }
753}
754
755#[derive(Debug, thiserror::Error)]
756#[error("could not parse range {attempted:?}")]
757pub struct RangeParseError {
758 attempted: String,
759}
760
761impl FromStr for Range {
762 type Err = RangeParseError;
763
764 fn from_str(s: &str) -> Result<Self, Self::Err> {
765 parse::range.parse(s).map_err(|_| RangeParseError {
766 attempted: s.to_owned(),
767 })
768 }
769}
770
771impl Serialize for Range {
772 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
773 where
774 S: serde::Serializer,
775 {
776 self.to_string().serialize(serializer)
777 }
778}
779
780impl<'de> Deserialize<'de> for Range {
781 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
782 where
783 D: serde::Deserializer<'de>,
784 {
785 let s = String::deserialize(deserializer)?;
786 Range::from_str(&s).map_err(serde::de::Error::custom)
787 }
788}
789
790impl From<IntRange> for Range {
791 fn from(range: IntRange) -> Self {
792 Self::IntRange(range)
793 }
794}
795
796impl From<FloatRange> for Range {
797 fn from(range: FloatRange) -> Self {
798 Self::FloatRange(range)
799 }
800}
801
802pub enum Iter {
803 IntIter(int_range::Iter, Span),
804 FloatIter(float_range::Iter, Span),
805}
806
807impl Iterator for Iter {
808 type Item = Value;
809
810 fn next(&mut self) -> Option<Self::Item> {
811 match self {
812 Iter::IntIter(iter, span) => iter.next().map(|val| Value::int(val, *span)),
813 Iter::FloatIter(iter, span) => iter.next().map(|val| Value::float(val, *span)),
814 }
815 }
816}
817
818mod parse {
819 use super::*;
820 use winnow::{
821 Result,
822 ascii::*,
823 combinator::*,
824 error::{StrContext, StrContextValue},
825 };
826
827 #[derive(Copy, Clone)]
828 enum Number {
829 Int(i64),
830 Float(f64),
831 }
832
833 fn number(input: &mut &str) -> Result<Number> {
835 fn float(input: &mut &str) -> Result<f64> {
836 (opt("-"), digit0, ".", digit1)
837 .take()
838 .parse_to()
839 .parse_next(input)
840 }
841
842 alt((float.map(Number::Float), dec_int.map(Number::Int))).parse_next(input)
843 }
844
845 struct Components {
846 start: Option<Number>,
847 step: Option<Number>,
848 end: Option<Number>,
849 exclusive: bool,
850 }
851
852 fn components(input: &mut &str) -> Result<Components> {
853 let start = opt(number).parse_next(input)?;
854 "..".parse_next(input)?;
855 if opt("<").parse_next(input)?.is_some() {
856 let end = opt(number).parse_next(input)?;
857 eof.parse_next(input)?;
858 return Ok(Components {
859 start,
860 step: None,
861 end,
862 exclusive: true,
863 });
864 }
865
866 if opt(eof).parse_next(input)?.is_some() {
867 return Ok(Components {
868 start,
869 step: None,
870 end: None,
871 exclusive: false,
872 });
873 }
874
875 let step_or_end = number.parse_next(input)?;
876 if opt(eof).parse_next(input)?.is_some() {
877 return Ok(Components {
878 start,
879 step: None,
880 end: step_or_end.into(),
881 exclusive: false,
882 });
883 }
884
885 "..".parse_next(input)?;
886 if opt("<").parse_next(input)?.is_some() {
887 let end = opt(number).parse_next(input)?;
888 eof.parse_next(input)?;
889 return Ok(Components {
890 start,
891 step: step_or_end.into(),
892 end,
893 exclusive: true,
894 });
895 }
896
897 let end = opt(number).parse_next(input)?;
898 eof.parse_next(input)?;
899 Ok(Components {
900 start,
901 step: step_or_end.into(),
902 end,
903 exclusive: false,
904 })
905 }
906
907 pub fn range(input: &mut &str) -> Result<Range> {
908 let components = components.parse_next(input)?;
909 if components.start.is_none() && components.end.is_none() {
910 fail.context(StrContext::Expected(StrContextValue::Description(
911 "needs bound either at start or end",
912 )))
913 .parse_next(input)?;
914 }
915
916 let use_float = matches!(components.start, Some(Number::Float(_)))
917 || matches!(components.step, Some(Number::Float(_)))
918 || matches!(components.end, Some(Number::Float(_)));
919
920 let range = if use_float {
921 let start = match components.start {
922 Some(Number::Float(start)) => Some(start),
923 Some(Number::Int(start)) => Some(start as f64),
924 None => None,
925 };
926
927 let step = match components.step {
928 Some(Number::Float(step)) => Some(step),
929 Some(Number::Int(step)) => Some(step as f64),
930 None => None,
931 };
932
933 let end = match (components.end, components.exclusive) {
934 (Some(Number::Float(end)), false) => Bound::Included(end),
935 (Some(Number::Float(end)), true) => Bound::Excluded(end),
936 (Some(Number::Int(end)), false) => Bound::Included(end as f64),
937 (Some(Number::Int(end)), true) => Bound::Excluded(end as f64),
938 (None, _) => Bound::Unbounded,
939 };
940
941 Range::new_float(start, step, end)
942 } else {
943 let start = match components.start {
944 Some(Number::Float(_)) => unreachable!("will use float if this is float"),
945 Some(Number::Int(start)) => Some(start),
946 None => None,
947 };
948
949 let step = match components.step {
950 Some(Number::Float(_)) => unreachable!("will use float if this is float"),
951 Some(Number::Int(step)) => Some(step),
952 None => None,
953 };
954
955 let end = match (components.end, components.exclusive) {
956 (Some(Number::Float(_)), _) => unreachable!("will use float if this is float"),
957 (Some(Number::Int(end)), false) => Bound::Included(end),
958 (Some(Number::Int(end)), true) => Bound::Excluded(end),
959 (None, _) => Bound::Unbounded,
960 };
961
962 Range::new_int(start, step, end)
963 };
964
965 Ok(range)
966 }
967}
968
969#[cfg(test)]
970mod tests {
971 use super::*;
972 use crate::Signals;
973
974 fn collect_float_range(start: f64, step: f64, end: f64, inclusive: bool) -> Vec<f64> {
975 let end = if inclusive {
976 Bound::Included(end)
977 } else {
978 Bound::Excluded(end)
979 };
980 let range = FloatRange { start, step, end };
981 range
982 .into_range_iter(Signals::empty())
983 .collect::<Vec<f64>>()
984 }
985
986 #[test]
987 fn float_range_small_step_inclusive() {
988 let result = collect_float_range(0.1, 0.1, 0.3, true);
989 assert_eq!(result.len(), 3);
990 assert!((result[0] - 0.1).abs() < 1e-15);
991 assert!((result[1] - 0.2).abs() < 1e-15);
992 assert!((result[2] - 0.3).abs() < 1e-15);
993 }
994
995 #[test]
996 fn float_range_tiny_step_inclusive() {
997 let result = collect_float_range(0.001, 0.001, 0.005, true);
998 assert_eq!(result.len(), 5);
999 assert!((result[0] - 0.001).abs() < 1e-15);
1000 assert!((result[1] - 0.002).abs() < 1e-15);
1001 assert!((result[2] - 0.003).abs() < 1e-15);
1002 assert!((result[3] - 0.004).abs() < 1e-15);
1003 assert!((result[4] - 0.005).abs() < 1e-15);
1004 }
1005
1006 #[test]
1007 fn float_range_integer_step_noninteger_start() {
1008 let result = collect_float_range(1.8, 1.0, 3.8, true);
1009 assert_eq!(result.len(), 3);
1010 assert!((result[0] - 1.8).abs() < 1e-15);
1011 assert!((result[1] - 2.8).abs() < 1e-15);
1012 assert!((result[2] - 3.8).abs() < 1e-15);
1013 }
1014
1015 #[test]
1016 fn float_range_decreasing() {
1017 let result = collect_float_range(0.3, -0.1, 0.1, true);
1018 assert_eq!(result.len(), 3);
1019 assert!((result[0] - 0.3).abs() < 1e-15);
1020 assert!((result[1] - 0.2).abs() < 1e-15);
1021 assert!((result[2] - 0.1).abs() < 1e-15);
1022 }
1023
1024 #[test]
1025 fn float_range_explicit_step_clean_values() {
1026 let result = collect_float_range(0.1, 0.2, 0.3, false);
1027 assert_eq!(result.len(), 1);
1028 assert!((result[0] - 0.1).abs() < 1e-15);
1029 }
1030
1031 #[test]
1032 fn float_range_rounds_last_value() {
1033 let result = collect_float_range(0.1, 0.1, 0.3, true);
1036 assert_eq!(result[2], 0.3);
1037 }
1038
1039 #[test]
1040 fn float_range_clean_serialization() {
1041 let result = collect_float_range(0.0, 0.1, 0.5, true);
1043 assert_eq!(result.len(), 6);
1044 for (i, &val) in result.iter().enumerate() {
1045 let expected = i as f64 * 0.1;
1046 assert!(
1047 (val - expected).abs() < 1e-15,
1048 "at index {i}: expected {expected}, got {val}"
1049 );
1050 }
1051 }
1052}