1use nom::error::ErrorKind;
2use std::ops::RangeInclusive;
3use std::str::FromStr;
4
5mod parser;
7
8#[derive(thiserror::Error, Debug, Clone, PartialEq)]
11#[non_exhaustive]
12pub enum Error {
13 #[error("Invalid token {}", 0)]
14 ParsingFailed(ErrorKind),
15}
16
17#[derive(Debug, PartialEq)]
19pub enum Selection {
20 All,
22 Some(Vec<SomeElementType>),
24 None,
26}
27
28impl Selection {
29 pub fn contains_item(&self, item: usize) -> bool {
32 match self {
33 Selection::Some(v) => {
34 v.iter()
35 .any(|element| {
36 match element {
37 SomeElementType::Individual(num) => item == *num,
38 SomeElementType::Range(range) => range.contains(&item),
39 }
40 })
41 },
42 Selection::All => true,
43 Selection::None => false,
44 }
45 }
46}
47
48#[derive(Debug, PartialEq)]
50pub enum SomeElementType {
51 Individual(usize),
52 Range(RangeInclusive<usize>),
53}
54
55pub use parser::parse as parse_raw;
56
57pub fn parse(input: &str) -> Result<Selection, Error> {
61 input.parse()
62}
63
64impl FromStr for Selection {
65 type Err = Error;
66
67 fn from_str(s: &str) -> Result<Self, Self::Err> {
68 Ok(match parse_raw(s)? {
69 Selection::Some(v) => Selection::Some(condense_selections(v)),
70 other => other,
71 })
72 }
73}
74
75fn condense_selections(selections: Vec<SomeElementType>) -> Vec<SomeElementType> {
78 let mut union = range_union_find::IntRangeUnionFind::new();
79 selections
80 .iter()
81 .map(|a| match a {
82 SomeElementType::Individual(num) => *num..=*num,
83 SomeElementType::Range(range) => range.clone(),
84 })
85 .filter(|a| !a.is_empty())
86 .try_for_each(|a| union.insert_range(&a))
87 .expect("bad ranges - shouldn't happen is bug");
88
89 let v = union.into_collection::<Vec<_>>();
90 v.into_iter()
91 .map(|a| {
92 if a.start() == a.end() {
93 SomeElementType::Individual(*a.start())
94 } else {
95 SomeElementType::Range(a)
96 }
97 })
98 .collect()
99}
100
101#[cfg(test)]
102mod helper_tests {
103 use super::*;
104
105 #[test]
106 fn selection_contains_item() {
107 assert!(Selection::All.contains_item(6543268));
108 assert!(!Selection::None.contains_item(385188));
109
110 assert!(Selection::Some(vec![SomeElementType::Individual(1)]).contains_item(1));
111 assert!(Selection::Some(vec![SomeElementType::Range(1..=1)]).contains_item(1));
112 assert!(Selection::Some(vec![SomeElementType::Range(1..=3)]).contains_item(1));
113 assert!(Selection::Some(vec![SomeElementType::Range(1..=3)]).contains_item(3));
114
115 let selection = Selection::Some(vec![
116 SomeElementType::Individual(2),
117 SomeElementType::Individual(6),
118 SomeElementType::Range(4..=8),
119 ]);
120 assert!(selection.contains_item(2));
121 assert!(selection.contains_item(6));
122 assert!(selection.contains_item(5));
123 assert!(selection.contains_item(8));
124 assert!(selection.contains_item(4));
125 assert!(!selection.contains_item(3));
126 assert!(!selection.contains_item(9));
127 assert!(!selection.contains_item(1));
128 assert!(!selection.contains_item(3));
129
130 }
131
132 #[test]
133 fn condense_ranges() {
134 let c = condense_selections(vec![
135 SomeElementType::Individual(1),
136 SomeElementType::Individual(3),
137 SomeElementType::Range(5..=9),
138 SomeElementType::Individual(8),
139 SomeElementType::Individual(10),
140 ]);
141
142 assert_eq!(
143 c,
144 vec![
145 SomeElementType::Individual(1),
146 SomeElementType::Individual(3),
147 SomeElementType::Range(5..=10),
148 ]
149 );
150 }
151
152 #[test]
153 fn condense_ranges_more_complex() {
154 let c = condense_selections(vec![
155 SomeElementType::Individual(1),
156 SomeElementType::Individual(3),
157 SomeElementType::Range(5..=9),
158 SomeElementType::Range(11..=20),
159 SomeElementType::Individual(10),
160 ]);
161
162 assert_eq!(
163 c,
164 vec![
165 SomeElementType::Individual(1),
166 SomeElementType::Individual(3),
167 SomeElementType::Range(5..=20),
168 ]
169 );
170 }
171}