1use crate::{Selection, SomeElementType};
2use nom::branch::alt;
3use nom::bytes::complete::{is_a, tag, tag_no_case};
4use nom::character::complete::{digit1, space1};
5
6use nom::combinator::{complete, cut, eof, map, map_res};
7
8use nom::multi::many1;
9use nom::sequence::tuple;
10use nom::IResult;
11
12use std::str::FromStr;
13
14fn select_none(input: &str) -> IResult<&str, Selection> {
16 fn none_literal(input: &str) -> IResult<&str, Selection> {
17 map(tuple((tag_no_case("none"), cut(eof))), |_| Selection::None)(input)
18 }
19 alt((map(eof, |_| Selection::None), none_literal))(input)
20}
21
22fn select_some(input: &str) -> IResult<&str, Selection> {
24 fn individual_element(input: &str) -> IResult<&str, SomeElementType> {
26 map(
27 map_res(digit1, usize::from_str),
28 SomeElementType::Individual,
29 )(input)
30 }
31 fn range_element(input: &str) -> IResult<&str, SomeElementType> {
33 map(
34 tuple((
35 map_res(digit1, usize::from_str),
36 is_a("-"),
37 map_res(cut(digit1), usize::from_str),
38 )),
39 |(start, _, end)| SomeElementType::Range(start..=end),
40 )(input)
41 }
42 fn some_element(input: &str) -> IResult<&str, SomeElementType> {
44 alt((range_element, individual_element))(input)
45 }
46 fn element_separator(input: &str) -> IResult<&str, &str> {
48 alt((map(many1(alt((tag(","), tag(";"), space1))), |_| ""), eof))(input)
49 }
50 map(
51 tuple((
52 many1(map(
53 tuple((some_element, element_separator)),
54 |(etype, _rem)| etype,
55 )),
56 eof,
57 )),
58 |a| Selection::Some(a.0),
59 )(input)
60}
61
62fn select_all(input: &str) -> IResult<&str, Selection> {
64 map(tuple((tag_no_case("all"), cut(eof))), |_| Selection::All)(input)
65}
66
67fn selection(input: &str) -> IResult<&str, Selection> {
69 complete(alt((select_none, select_all, select_some)))(input)
70}
71
72pub fn parse(input: &str) -> Result<Selection, crate::Error> {
75 match selection(input) {
76 Ok((_, sel)) => Ok(sel),
77 Err(err) => {
78 if let nom::Err::Failure(error) = err {
79 Err(crate::Error::ParsingFailed(error.code))
80 } else {
81 panic!("Internal parser error");
82 }
83 }
84 }
85}
86
87#[cfg(test)]
88mod parser_tests {
89
90 use super::*;
91 use nom::error::ErrorKind;
92
93 macro_rules! does_parse {
94 ($input:literal, $name:ident) => {
95 #[test]
96 fn $name() {
97 selection($input).unwrap();
98 }
99 };
100 }
101
102 does_parse!("all", all);
103 does_parse!("none", none);
104 does_parse!("", none_no_input);
105 does_parse!("1", single_digit);
106 does_parse!("1-9", single_range);
107 does_parse!("8-2", inverse_range);
108 does_parse!("1-90", single_range_multi_digit_end);
109 does_parse!("10-90", single_range_multi_digit_start_end);
110 does_parse!("10-0", single_range_multi_digit_start);
111
112 does_parse!("1 2 3", multiple_individuals);
113 does_parse!("1-3 5-8", multiple_ranges);
114 does_parse!("1 5 8", multiple_separators);
115 does_parse!("1,,,,5,8", multiple_separators_2);
116 does_parse!("1;;;;5;;;;8", multiple_separators_3);
117 does_parse!("1;,,;5 ,; ;8", multiple_separators_mixed);
118 does_parse!("1;5;8", separators_1);
119 does_parse!("1,5,8", separators_2);
120
121 does_parse!("1-10 15 20", mixed_elements);
122
123 #[test]
124 fn fails_broken_range_start() {
125 selection("1-").unwrap_err();
126 }
127
128 #[test]
129 fn fails_broken_range_both() {
130 selection("-").unwrap_err();
131 }
132
133 #[test]
134 fn fails_broken_range_end() {
135 selection("-5").unwrap_err();
136 }
137
138 #[test]
139 fn content_none() {
140 assert_eq!(parse("").unwrap(), Selection::None);
141 }
142
143 #[test]
144 fn content_none2() {
145 assert_eq!(parse("none").unwrap(), Selection::None);
146 }
147
148 #[test]
149 fn content_all() {
150 assert_eq!(parse("all").unwrap(), Selection::All);
151 }
152
153 #[test]
154 fn content_individual() {
155 assert_eq!(
156 parse("8").unwrap(),
157 Selection::Some(vec![SomeElementType::Individual(8)])
158 );
159 }
160
161 #[test]
162 fn content_individual_multi() {
163 assert_eq!(
164 parse("8 9 10").unwrap(),
165 Selection::Some(vec![
166 SomeElementType::Individual(8),
167 SomeElementType::Individual(9),
168 SomeElementType::Individual(10)
169 ])
170 );
171 }
172
173 #[test]
174 fn content_individual_multi_ranges_individuals() {
175 assert_eq!(
176 parse("8 9-12 4").unwrap(),
177 Selection::Some(vec![
178 SomeElementType::Individual(8),
179 SomeElementType::Range(9..=12),
180 SomeElementType::Individual(4)
181 ])
182 );
183 }
184
185 #[test]
186 fn test_error() {
187 let err = parse("1 3 5 6-8 1-;455").unwrap_err();
188 match err {
189 crate::Error::ParsingFailed(kind) => assert_eq!(kind, ErrorKind::Digit),
190 #[allow(unreachable_patterns)]
191 _ => panic!("Wrong kind"),
192 }
193 }
194}