1use anyhow::{Context, Result};
24use itertools::Itertools;
25use std::collections::VecDeque;
26
27#[derive(Debug)]
28pub struct NumberRangeError;
29
30impl std::error::Error for NumberRangeError {}
31
32impl std::fmt::Display for NumberRangeError {
33 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
34 write!(f, "Invalid number range")
35 }
36}
37
38#[derive(Debug)]
57pub enum Number<T> {
58 Single(T),
59 Range(T, T, T),
60}
61
62impl<T: num::Zero + std::cmp::PartialOrd + Copy> Number<T> {
63 pub fn is_valid(&self) -> bool {
77 match self {
78 Number::Single(_) => true,
79 Number::Range(start, step, end) => {
80 ((start <= end) && (step > &num::Zero::zero()))
81 || ((start >= end) && (step < &num::Zero::zero()))
82 }
83 }
84 }
85 pub fn is_invalid(&self) -> bool {
98 !self.is_valid()
99 }
100}
101
102#[derive(Debug)]
193pub struct NumberRangeOptions<T> {
194 pub group_sep: char,
199 pub whitespace: bool,
203 pub decimal_sep: char,
207 pub list_sep: char,
210 pub range_sep: char,
215 pub default_start: Option<T>,
218 pub default_end: Option<T>,
221}
222
223#[derive(Debug)]
263pub struct NumberRange<'a, T> {
264 pub numbers: VecDeque<Number<T>>,
265 original_repr: Option<&'a str>,
266 pub options: NumberRangeOptions<T>,
267}
268
269impl<'a, T: std::fmt::Display + num::One + std::cmp::PartialEq> std::fmt::Display
270 for NumberRange<'a, T>
271{
272 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
273 let repr = self
274 .numbers
275 .iter()
276 .map(|n| match n {
277 Number::Single(v) => format!("{}", v),
278 Number::Range(s, i, e) => {
279 if i.is_one() {
280 format!("{}{}{}", s, self.options.range_sep, e)
281 } else {
282 format!("{}{}{}{1}{}", s, self.options.range_sep, i, e)
283 }
284 }
285 })
286 .join(&self.options.list_sep.to_string());
287 write!(f, "{}", repr)
288 }
289}
290
291impl<'a, T: Copy + std::ops::Add<Output = T> + std::cmp::PartialOrd + num::Zero> Iterator
292 for NumberRange<'a, T>
293{
294 type Item = T;
295
296 fn next(&mut self) -> Option<T> {
297 if self.numbers.is_empty() {
298 return None;
299 }
300 match self.numbers[0] {
301 Number::Single(v) => {
302 self.numbers.pop_front();
303 Some(v)
304 }
305 Number::Range(start, step, end) => {
306 if self.numbers[0].is_valid() {
308 let next_step = Number::Range(start + step, step, end);
309 if next_step.is_valid() {
311 self.numbers[0] = next_step;
312 } else {
313 self.numbers.pop_front();
314 }
315 Some(start)
316 } else {
317 self.numbers.pop_front();
318 self.next()
319 }
320 }
321 }
322 }
323}
324
325impl<
326 T: std::str::FromStr
327 + num::One
328 + Copy
329 + std::str::FromStr
330 + std::cmp::PartialOrd
331 + std::ops::Add<Output = T>,
332 > Default for NumberRangeOptions<T>
333{
334 fn default() -> Self {
335 Self::new()
336 }
337}
338
339impl<T: std::str::FromStr + num::One + Copy + std::cmp::PartialOrd + std::ops::Add<Output = T>>
340 NumberRangeOptions<T>
341{
342 pub fn new() -> Self {
344 Self {
345 list_sep: ',',
346 range_sep: ':',
347 decimal_sep: '.',
348 group_sep: '_',
349 whitespace: false,
350 default_start: None,
351 default_end: None,
352 }
353 }
354
355 pub fn with_group_sep(mut self, sep: char) -> Self {
357 self.group_sep = sep;
358 self
359 }
360
361 pub fn with_whitespace(mut self, flag: bool) -> Self {
363 self.whitespace = flag;
364 self
365 }
366
367 pub fn with_decimal_sep(mut self, sep: char) -> Self {
369 self.decimal_sep = sep;
370 self
371 }
372
373 pub fn with_list_sep(mut self, sep: char) -> Self {
375 self.list_sep = sep;
376 self
377 }
378
379 pub fn with_range_sep(mut self, sep: char) -> Self {
381 self.range_sep = sep;
382 self
383 }
384
385 pub fn with_default_start(mut self, def: T) -> Self {
387 self.default_start = Some(def);
388 self
389 }
390
391 pub fn with_default_end(mut self, def: T) -> Self {
393 self.default_end = Some(def);
394 self
395 }
396
397 pub fn parse<'a>(self, numstr: &'a str) -> Result<NumberRange<T>>
400 where
401 <T as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static,
402 {
403 let nr = NumberRange::from_options(self);
404 nr.parse_str(numstr)
405 }
406}
407
408impl<
409 'a,
410 T: num::One
411 + std::str::FromStr
412 + num::One
413 + Copy
414 + std::cmp::PartialOrd
415 + std::ops::Add<Output = T>,
416 > Default for NumberRange<'a, T>
417{
418 fn default() -> Self {
421 Self {
422 numbers: VecDeque::new(),
423 original_repr: None,
424 options: NumberRangeOptions::default(),
425 }
426 }
427}
428
429impl<
430 'a,
431 T: std::str::FromStr + num::One + Copy + std::cmp::PartialOrd + std::ops::Add<Output = T>,
432 > NumberRange<'a, T>
433where
434 <T as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static,
435{
436 pub fn from_options(options: NumberRangeOptions<T>) -> Self {
438 Self {
439 numbers: VecDeque::new(),
440 original_repr: None,
441 options,
442 }
443 }
444
445 pub fn original(&self) -> &str {
447 self.original_repr.unwrap_or("")
448 }
449
450 pub fn parse_str(mut self, numstr: &'a str) -> Result<Self> {
465 self.original_repr = Some(numstr);
466 self.parse()
467 }
468
469 pub fn from_vec<V>(self, nums: V, increment: Option<T>) -> Self
470 where
471 T: std::cmp::Ord,
472 V: IntoIterator<Item = T>,
473 {
474 let mut nums: Vec<T> = nums.into_iter().collect();
475 nums.sort();
476 self.from_vec_nosort(&nums, increment)
477 }
478
479 pub fn from_vec_nosort(mut self, nums: &[T], increment: Option<T>) -> Self
480 where
481 T: std::cmp::Ord,
482 {
483 self.original_repr = None;
484 let inc = increment.unwrap_or(num::one());
485 self.numbers.clear();
486 if nums.len() > 0 {
487 let mut first = &nums[0];
488 let mut prev = &nums[0];
489 let mut rng = false;
490 for current in &nums[1..] {
491 if current == prev {
492 continue;
493 }
494 if *current == (*prev + inc) {
495 if !rng {
496 rng = true;
497 first = prev;
498 }
499 } else {
500 if rng {
501 self.numbers.push_back(Number::Range(*first, inc, *prev));
502 } else {
503 self.numbers.push_back(Number::Single(*prev));
504 }
505 rng = false;
506 }
507 prev = current;
508 }
509 if rng {
510 self.numbers.push_back(Number::Range(*first, inc, *prev));
511 } else {
512 self.numbers.push_back(Number::Single(*prev));
513 }
514 }
515 self
516 }
517
518 fn sanitize_number(&self, num: &str) -> String {
519 let num = num.trim().replace(self.options.group_sep, "");
520 let num = if self.options.whitespace {
521 num.split_whitespace().join("")
522 } else {
523 num
524 };
525 num.replace(self.options.decimal_sep, ".")
526 }
527
528 fn parse_number(&self, num: &str, def: &Option<T>) -> Result<T> {
529 let s = self.sanitize_number(num);
530 if def.is_some() && s == "" {
531 return Ok(def.unwrap());
532 } else {
533 s.parse::<T>()
534 .with_context(|| format!("{} Not a Number", num))
535 }
536 }
537
538 pub fn parse(mut self) -> Result<Self> {
539 if let Some(numstr) = self.original_repr {
540 if self.sanitize_number(numstr) == "" {
541 self.numbers.clear();
542 return Ok(self);
543 }
544 let numbers: VecDeque<Number<T>> = numstr
545 .split(self.options.list_sep)
546 .map(|seq_str| -> Result<Number<T>> {
547 match seq_str.matches(self.options.range_sep).count() {
548 0 => self.parse_number(seq_str, &None).map(|v| Number::Single(v)),
549 1 => match seq_str.split_once(self.options.range_sep) {
550 Some((start, end)) => {
551 let start =
552 self.parse_number(start, &self.options.default_start)?;
553 let end = self.parse_number(end, &self.options.default_end)?;
554 Ok(Number::Range(start, num::One::one(), end))
555 }
556 None => panic!(
557 "Checked there is single range_separator, yet split to 2 failed."
558 ),
559 },
560 2 => {
561 let nums: Vec<T> = seq_str
562 .splitn(3, self.options.range_sep)
563 .enumerate()
564 .map(|(i, s)| -> Result<T> {
565 self.parse_number(
566 s,
567 [
568 &self.options.default_start,
569 &Some(num::One::one()),
570 &self.options.default_end,
571 ][i],
572 )
573 })
574 .collect::<Result<Vec<T>>>()?;
575 Ok(Number::Range(nums[0], nums[1], nums[2]))
576 }
577 _ => Err::<Number<_>, anyhow::Error>(NumberRangeError {}.into())
578 .with_context(|| {
579 format!(
580 "Too many range separators ({}) on {}",
581 self.options.range_sep, seq_str
582 )
583 }),
584 }
585 })
586 .collect::<Result<VecDeque<Number<T>>>>()?;
587 self.numbers = numbers;
588 Ok(self)
589 } else {
590 Err::<NumberRange<'_, _>, anyhow::Error>(NumberRangeError {}.into())
591 .with_context(|| "Nothing to Parse".to_string())
592 }
593 }
594}
595
596#[macro_export]
612macro_rules! numrng {
613 ($($l:tt)*) => {
614 $crate::NumberRangeOptions::new().with_whitespace(true).parse(stringify!($($l)*)).unwrap()
616 };
617}
618
619#[macro_export]
634macro_rules! numvec {
635 ($($l:tt)*) => {
636 $crate::numrng!($($l)*)
637 .collect()
638 };
639}
640
641#[cfg(test)]
642use rstest::rstest;
643
644#[cfg(test)]
645mod tests {
646 use super::*;
647
648 #[rstest]
649 fn manual_build() {
650 let mut rng = NumberRange::<i64>::default();
651 rng.numbers.push_back(Number::Single(1));
652 rng.numbers.push_back(Number::Range(3, 2, 6));
653 rng.numbers.push_back(Number::Range(-4, 1, -2));
654 assert_eq!(format!("{}", rng), "1,3:2:6,-4:-2");
655 assert_eq!(rng.collect::<Vec<i64>>(), vec![1, 3, 5, -4, -3, -2]);
656 }
657
658 #[rstest]
659 fn options_build() {
660 let rng: NumberRange<usize> = NumberRangeOptions::<usize>::default()
661 .with_list_sep('*')
662 .with_range_sep('/')
663 .parse("1*3/5*9/2/15")
664 .expect("Parsing should be succesful");
665 assert_eq!(rng.collect::<Vec<usize>>(), vec![1, 3, 4, 5, 9, 11, 13, 15]);
666 }
667
668 #[rstest]
669 fn manual_build_then_modify() {
670 let mut rng = NumberRange::<i64>::default();
671 rng.numbers.push_back(Number::Single(1));
672 rng.numbers.push_back(Number::Range(3, 2, 6));
673 rng.numbers.push_back(Number::Range(-4, 1, -2));
674 let values = vec![1, 3, 5, -4, -3, -2];
675 assert_eq!(format!("{}", rng), "1,3:2:6,-4:-2");
676 for i in 0..4 {
677 assert_eq!(rng.next().expect("Should have next"), values[i]);
678 }
679 assert_eq!(format!("{}", rng), "-3:-2");
680 rng.numbers.push_back(Number::Single(1));
681 assert_eq!(format!("{}", rng), "-3:-2,1");
682 assert_eq!(rng.collect::<Vec<i64>>(), vec![-3, -2, 1]);
683 }
684
685 #[rstest]
686 fn options_build_then_modify() {
687 let mut rng: NumberRange<usize> = NumberRangeOptions::<usize>::default()
688 .with_list_sep(':')
689 .with_range_sep('-')
690 .parse("1:3-5:9")
691 .expect("Parsing should be succesful");
692 let values = vec![1, 3, 4, 5, 9];
693 for i in 0..4 {
694 assert_eq!(rng.next().expect("Should have next"), values[i]);
695 }
696 assert_eq!(format!("{}", rng), "9");
697 rng.numbers.push_back(Number::Range(11, 2, 15));
698 assert_eq!(format!("{}", rng), "9:11-2-15");
699 assert_eq!(rng.collect::<Vec<usize>>(), vec![9, 11, 13, 15]);
700 }
701
702 #[rstest]
703 #[case("200", vec![200])]
704 #[case("-200", vec![-200])]
705 #[case("1,4", vec![1, 4])]
706 #[case("1, -4", vec![1, -4])]
707 #[should_panic]
708 #[case("1.0, 2", vec![])]
709 #[case("1: 3", vec![1, 2, 3])]
710 #[case(" -1:10", vec![-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])]
711 #[case("3 , 5:10", vec![3, 5, 6, 7, 8, 9, 10])]
712 fn comma_default_int(#[case] numstr: &str, #[case] numvec: Vec<i64>) {
713 assert_eq!(
714 NumberRange::<i64>::default()
715 .parse_str(numstr)
716 .unwrap()
717 .collect::<Vec<i64>>(),
718 numvec
719 );
720 }
721
722 #[rstest]
723 fn limits_test() {
724 let nr1: Vec<usize> = NumberRangeOptions::<usize>::new()
725 .with_range_sep('-')
726 .with_default_start(0)
727 .parse("-2")
728 .unwrap()
729 .collect();
730 assert_eq!(nr1, vec![0, 1, 2]);
731 let nr1: Vec<usize> = NumberRangeOptions::<usize>::new()
732 .with_range_sep('-')
733 .with_default_end(5)
734 .parse("2-")
735 .unwrap()
736 .collect();
737 assert_eq!(nr1, vec![2, 3, 4, 5]);
738 let nr1: Vec<usize> = NumberRangeOptions::<usize>::new()
739 .with_range_sep('-')
740 .with_default_start(0)
741 .with_default_end(5)
742 .parse("-")
743 .unwrap()
744 .collect();
745 assert_eq!(nr1, vec![0, 1, 2, 3, 4, 5]);
746 }
747
748 #[rstest]
749 #[case("200", vec![200])]
750 #[case("1,4", vec![1, 4])]
751 #[should_panic]
752 #[case("1,-4", vec![])]
753 #[should_panic]
754 #[case(",4", vec![])]
755 #[should_panic]
756 #[case("1,,4", vec![])]
757 #[should_panic]
758 #[case("1,4,", vec![])]
759 #[should_panic]
760 #[case("1,4.0", vec![])]
761 #[case("1:3", vec![1, 2, 3])]
762 #[case("1:10", vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10])]
763 fn default_usize(#[case] numstr: &str, #[case] numvec: Vec<usize>) {
764 assert_eq!(
765 NumberRange::<usize>::default()
766 .parse_str(numstr)
767 .unwrap()
768 .collect::<Vec<usize>>(),
769 numvec
770 );
771 }
772
773 #[rstest]
774 #[case("200", vec!["200"])]
775 #[case("1,4", vec!["1,4", "4"])]
776 #[case("1:4", vec!["1:4", "2:4", "3:4", "4:4"])]
777 #[case("10:-4:4", vec!["10:-4:4", "6:-4:4"])]
778 #[case("4:-1:1", vec!["4:-1:1", "3:-1:1", "2:-1:1", "1:-1:1"])]
779 #[case("1:4:10", vec!["1:4:10", "5:4:10", "9:4:10"])]
780 fn format_test_loop(#[case] numstr: &str, #[case] numvec: Vec<&str>) {
781 let mut rng: NumberRange<i64> = NumberRange::default().parse_str(numstr).unwrap();
782 for fmt_str in numvec {
783 assert_eq!(fmt_str, format!("{}", &rng));
784 rng.next();
785 }
786 assert!(rng.next().is_none());
787 }
788
789 #[rstest]
790 #[case("200", ',',vec![200])]
791 #[case("1,4", ',', vec![1, 4])]
792 #[case("1:4", ':', vec![1, 4])]
793 #[case("1:4:4", ':', vec![1, 4, 4])]
794 #[case("1/4", '/', vec![1, 4])]
795 #[should_panic]
796 #[case("1--4", '-', vec![])]
797 #[should_panic]
798 #[case("1,-4", ':', vec![])]
799 fn comma_test_sep_usize(#[case] numstr: &str, #[case] sep: char, #[case] numvec: Vec<usize>) {
800 assert_eq!(
801 NumberRangeOptions::<usize>::new()
802 .with_list_sep(sep)
803 .parse(numstr)
804 .unwrap()
805 .collect::<Vec<usize>>(),
806 numvec
807 );
808 }
809
810 #[rstest]
811 #[case("200", '-',vec![200])]
812 #[case("1-4", '-', vec![1,2,3,4])]
813 #[case("1:3:4", ':', vec![1, 4])]
814 #[should_panic]
815 #[case("4:-3:1", ':', vec![])]
816 #[should_panic]
817 #[case("1--4", '-', vec![])]
818 fn comma_test_range_usize(#[case] numstr: &str, #[case] sep: char, #[case] numvec: Vec<usize>) {
819 assert_eq!(
820 NumberRangeOptions::<usize>::new()
821 .with_range_sep(sep)
822 .parse(numstr)
823 .unwrap()
824 .collect::<Vec<usize>>(),
825 numvec
826 );
827 }
828
829 #[rstest]
830 #[case("200", '-',vec![200])]
831 #[case("1-4", '-', vec![1,2,3,4])]
832 #[case("1:3:4", ':', vec![1, 4])]
833 #[case("4:-3:1", ':', vec![4, 1])]
834 #[case("-4:1", ':', vec![-4, -3, -2, -1, 0, 1])]
835 #[case("1:-4", ':', vec![])]
836 fn comma_test_range_i64(#[case] numstr: &str, #[case] sep: char, #[case] numvec: Vec<i64>) {
837 assert_eq!(
838 NumberRangeOptions::<i64>::new()
839 .with_range_sep(sep)
840 .parse(numstr)
841 .unwrap()
842 .collect::<Vec<i64>>(),
843 numvec
844 );
845 }
846
847 #[rstest]
848 #[case("200", '-',vec![200.0])]
849 #[case("1-4", '-', vec![1.0,2.0,3.0,4.0])]
850 #[case("1:3:4", ':', vec![1.0, 4.0])]
851 #[case("1:.5:4", ':', vec![1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0])]
852 #[case("4:-3:1", ':', vec![4.0, 1.0])]
853 #[case("-4:1", ':', vec![-4.0, -3.0, -2.0, -1.0, 0.0, 1.0])]
854 #[case("1:-4", ':', vec![])]
855 fn comma_test_range_f64(#[case] numstr: &str, #[case] sep: char, #[case] numvec: Vec<f64>) {
856 assert_eq!(
857 NumberRangeOptions::<f64>::new()
858 .with_range_sep(sep)
859 .parse(numstr)
860 .unwrap()
861 .collect::<Vec<f64>>(),
862 numvec
863 );
864 }
865
866 #[rstest]
867 fn comma_test_empty_range() {
868 assert_eq!(
869 NumberRange::default()
870 .parse_str("")
871 .unwrap()
872 .collect::<Vec<f64>>(),
873 vec![]
874 );
875 let rng = NumberRange::default().parse_str("1:10").unwrap();
877 assert_eq!(rng.parse_str("").unwrap().collect::<Vec<f64>>(), vec![]);
878 }
879
880 #[rstest]
881 #[case([1,2,3], None, "1:3")]
882 #[case(vec![1,2,3], None, "1:3")]
883 #[case([1,3,5,7], None, "1,3,5,7")]
884 #[case([1,3,5,7,10], Some(2), "1:2:7,10")]
885 fn rng_from_vec(
886 #[case] inp: impl IntoIterator<Item = i64>,
887 #[case] inc: Option<i64>,
888 #[case] s: &str,
889 ) {
890 assert_eq!(format!("{}", NumberRange::default().from_vec(inp, inc)), s);
891 }
892}