1use itertools::Itertools;
2use std::collections::HashMap;
3use std::str::CharIndices;
4use std::iter::Peekable;
5
6use super::ParseError;
7
8pub type LabelMap<'a> = HashMap<&'a str, &'a str>;
10
11fn parse_key(index: &mut usize, chars: &mut Peekable<CharIndices>) -> Result<String, ParseError> {
12 let key: String = chars
14 .take_while_ref(|&(_i, c)| match c {
15 'a'...'z' | 'A'...'Z' | '0'...'9' | '-' | '_' => true,
16 _ => false,
17 })
18 .map(|(i, c)| {
19 *index = i;
20 c
21 })
22 .collect();
23
24 if key.is_empty() {
25 let next = chars.peek();
26 if let Some(&(i, c)) = next {
27 *index = i;
28 return match c {
29 ',' => Ok(key),
30 _ => Err(ParseError::InvalidKey(i)),
31 };
32 } else {
33 return Err(ParseError::InvalidKey(*index));
34 }
35 }
36 Ok(key)
37}
38
39fn skip_whitespaces(index: &mut usize, chars: &mut Peekable<CharIndices>) {
40 while let Some(&(i, c)) = chars.peek() {
42 *index = i;
43 if c == ' ' {
44 chars.next();
45 } else {
46 break;
47 }
48 }
49}
50
51#[derive(Debug, PartialEq)]
52enum Operator {
53 Equality,
54 InEquality,
55 InSet,
56 NotInSet,
57}
58
59fn parse_operator(
60 index: &mut usize,
61 chars: &mut Peekable<CharIndices>,
62) -> Result<Operator, ParseError> {
63 match chars.next() {
64 Some((i, c)) => {
65 *index = i;
66 match c {
67 '=' => {
68 if let Some(&(i, c)) = chars.peek() {
69 if c == '=' {
70 chars.next();
71 *index = i;
72 }
73 }
74 Ok(Operator::Equality)
75 }
76 '!' => match chars.next() {
77 Some((i, c)) => {
78 *index = i;
79 match c {
80 '=' => Ok(Operator::InEquality),
81 _ => Err(ParseError::InvalidOperator(i)),
82 }
83 }
84 None => Err(ParseError::InvalidOperator(i)),
85 },
86 'i' => match chars.next() {
87 Some((i, c)) => {
88 *index = i;
89 match c {
90 'n' => Ok(Operator::InSet),
91 _ => Err(ParseError::InvalidOperator(i)),
92 }
93 }
94 None => Err(ParseError::InvalidOperator(i)),
95 },
96 'n' => {
97 let s: String = chars.take(4).map(|(_i, c)| c).collect();
98
99 if s == "otin" {
100 *index += 5;
101 Ok(Operator::NotInSet)
102 } else {
103 Err(ParseError::InvalidOperator(i))
104 }
105 }
106 _ => Err(ParseError::InvalidOperator(i)),
107 }
108 }
109 None => Err(ParseError::InvalidOperator(*index)),
110 }
111}
112
113fn parse_value(index: &mut usize, chars: &mut Peekable<CharIndices>) -> Result<String, ParseError> {
114 let value: String = chars
115 .take_while_ref(|&(_i, c)| match c {
116 '!' | '=' | ',' | '(' | ')' => false,
117 _ => !c.is_whitespace(),
118 })
119 .map(|(i, c)| {
120 *index = i;
121 c
122 })
123 .collect();
124
125 if value.is_empty() {
126 Err(ParseError::ExpectingValue(*index))
127 } else {
128 Ok(value)
129 }
130}
131
132fn parse_set(
133 index: &mut usize,
134 chars: &mut Peekable<CharIndices>,
135) -> Result<Vec<String>, ParseError> {
136 let mut result = vec![];
137
138 let parens = chars.next();
139 match parens {
140 None => Err(ParseError::ExpectingLeftParenthesis(*index)),
141 Some((i, c)) => {
142 *index = i;
143 match c {
144 '(' => Ok(()),
145 _ => Err(ParseError::ExpectingLeftParenthesis(i)),
146 }
147 }
148 }?;
149
150 let mut first = true;
151 loop {
152 skip_whitespaces(index, chars);
153 if !first {
154 match chars.next() {
155 None => return Err(ParseError::ExpectingEndOrComma(*index)),
156 Some((i, c)) => {
157 *index = i;
158 match c {
159 ')' => {
160 break;
161 }
162 ',' => (),
163 _ => {
164 return Err(ParseError::ExpectingEndOrComma(i));
165 }
166 }
167 }
168 }
169 }
170 first = false;
171 result.push(parse_value(index, chars)?);
172 }
173 Ok(result)
174}
175
176fn compare_value(label: Option<&&str>, value: &str) -> bool {
177 match label {
178 None => false,
179 Some(label) => *label == value,
180 }
181}
182
183fn compare_set(label: Option<&&str>, value: &[String]) -> bool {
184 match label {
185 None => false,
186 Some(label) => value.contains(&String::from(*label)),
187 }
188}
189
190fn parse_operation(
191 index: &mut usize,
192 label: Option<&&str>,
193 chars: &mut Peekable<CharIndices>,
194) -> Result<bool, ParseError> {
195 let operator = parse_operator(index, chars)?;
196 skip_whitespaces(index, chars);
197
198 Ok(match operator {
199 Operator::Equality => compare_value(label, &parse_value(index, chars)?),
200 Operator::InEquality => !compare_value(label, &parse_value(index, chars)?),
201 Operator::InSet => compare_set(label, &parse_set(index, chars)?),
202 Operator::NotInSet => !compare_set(label, &parse_set(index, chars)?),
203 })
204}
205
206fn parse_requirement(
207 index: &mut usize,
208 chars: &mut Peekable<CharIndices>,
209 labels: &LabelMap,
210) -> Result<bool, ParseError> {
211 skip_whitespaces(index, chars);
212
213 let mut positive = true;
214 if let Some(&(i, c)) = chars.peek() {
215 if c == '!' {
216 *index = i;
217 positive = false;
218 chars.next();
219 skip_whitespaces(index, chars);
220 }
221 }
222 let key = parse_key(index, chars)?;
223 skip_whitespaces(index, chars);
224
225 let simple_exists_check = {
226 let next = chars.peek();
227 match next {
228 None => true,
229 Some(&(i, c)) => {
230 *index = i;
231 match c {
232 ',' => true,
233 _ => false,
234 }
235 }
236 }
237 };
238 let value = if simple_exists_check {
239 labels.contains_key(key.as_str())
240 } else {
241 parse_operation(index, labels.get(key.as_str()), chars)?
242 };
243
244 Ok(if positive { value } else { !value })
245}
246
247fn parse_selector(selector: &str, labels: &LabelMap) -> Result<bool, ParseError> {
248 let mut chars = selector.char_indices().peekable();
249
250 let mut index = 0;
251 let mut result = parse_requirement(&mut index, &mut chars, labels)?;
252 loop {
253 skip_whitespaces(&mut index, &mut chars);
254 if let Some(&(i, c)) = chars.peek() {
255 if c == ',' {
256 chars.next();
257 }
258 index = i;
259 } else {
260 break;
261 }
262 result = parse_requirement(&mut index, &mut chars, labels)? && result;
263 }
264 Ok(result)
265}
266
267pub fn parse(selector: &str, labelmap: &LabelMap) -> Result<bool, ParseError> {
271 if selector.is_empty() {
272 return Err(ParseError::EmptySelector);
273 }
274 parse_selector(selector, labelmap)
275}
276
277#[cfg(test)]
278mod tests {
279 #[test]
280 fn it_should_properly_parse_a_full_key() {
281 let selector = "test";
282
283 let mut index = 0;
284 let mut char_indices = selector.char_indices().peekable();
285 let result = super::parse_key(&mut index, &mut char_indices);
286 assert!(result.is_ok());
287 assert_eq!(result.unwrap(), "test");
288 }
289
290 #[test]
291 fn it_will_parse_keys_until_invalid_chars() {
292 let selector = "test/";
293
294 let mut index = 0;
295 let mut char_indices = selector.char_indices().peekable();
296 let result = super::parse_key(&mut index, &mut char_indices);
297 assert!(result.is_ok());
298 assert_eq!(result.unwrap(), "test");
299
300 let result = super::parse_key(&mut index, &mut char_indices);
301 assert_matches!(result, Err(super::ParseError::InvalidKey(4)));
302 }
303
304 #[test]
305 fn it_will_accept_valid_keys_but_stop_consuming() {
306 let selector = "test /";
307
308 let mut index = 0;
309 let mut char_indices = selector.char_indices().peekable();
310 let key = super::parse_key(&mut index, &mut char_indices);
311 assert_eq!(key.unwrap(), "test");
312 assert_matches!(char_indices.next(), Some((4, ' ')));
313 }
314
315 #[test]
316 fn it_will_parse_single_char_equality() {
317 let op = "=";
318
319 let mut index = 0;
320 let mut char_indices = op.char_indices().peekable();
321 let result = super::parse_operator(&mut index, &mut char_indices);
322 assert!(result.is_ok());
323 assert_eq!(result.unwrap(), super::Operator::Equality);
324 }
325
326 #[test]
327 fn it_will_parse_double_char_equality() {
328 let op = "==";
329
330 let mut index = 0;
331 let mut char_indices = op.char_indices().peekable();
332 let result = super::parse_operator(&mut index, &mut char_indices);
333 assert!(result.is_ok());
334 assert_eq!(result.unwrap(), super::Operator::Equality);
335
336 assert!(char_indices.next().is_none());
337 }
338
339 #[test]
340 fn it_will_reject_garbage() {
341 let op = "#";
342
343 let mut index = 0;
344 let mut char_indices = op.char_indices().peekable();
345 let result = super::parse_operator(&mut index, &mut char_indices);
346 assert_matches!(result, Err(super::ParseError::InvalidOperator(0)));
347 }
348
349 #[test]
350 fn it_will_parse_inequality() {
351 let op = "!=";
352
353 let mut index = 0;
354 let mut char_indices = op.char_indices().peekable();
355 let result = super::parse_operator(&mut index, &mut char_indices);
356 assert!(result.is_ok());
357 assert_eq!(result.unwrap(), super::Operator::InEquality);
358
359 assert!(char_indices.next().is_none());
360 }
361
362 #[test]
363 fn it_will_reject_garbage_inequality() {
364 let op = "!#";
365
366 let mut index = 0;
367 let mut char_indices = op.char_indices().peekable();
368 let result = super::parse_operator(&mut index, &mut char_indices);
369 assert!(result.is_err());
370 assert_matches!(result, Err(super::ParseError::InvalidOperator(1)));
371 }
372
373 #[test]
374 fn it_will_accept_in() {
375 let op = "in";
376
377 let mut index = 0;
378 let mut char_indices = op.char_indices().peekable();
379 let result = super::parse_operator(&mut index, &mut char_indices);
380 assert!(result.is_ok());
381 assert_eq!(result.unwrap(), super::Operator::InSet);
382 }
383
384 #[test]
385 fn it_will_accept_notin() {
386 let op = "notin";
387
388 let mut index = 0;
389 let mut char_indices = op.char_indices().peekable();
390 let result = super::parse_operator(&mut index, &mut char_indices);
391 assert!(result.is_ok());
392 assert_eq!(result.unwrap(), super::Operator::NotInSet);
393 }
394
395 #[test]
396 fn it_will_refuse_garbage_in() {
397 let op = "ian";
398
399 let mut index = 0;
400 let mut char_indices = op.char_indices().peekable();
401 let result = super::parse_operator(&mut index, &mut char_indices);
402 assert_matches!(result, Err(super::ParseError::InvalidOperator(1)));
403 }
404
405 #[test]
406 fn it_will_refuse_garbage_notin() {
407 let op = "no";
408
409 let mut index = 0;
410 let mut char_indices = op.char_indices().peekable();
411 let result = super::parse_operator(&mut index, &mut char_indices);
412 assert_matches!(result, Err(super::ParseError::InvalidOperator(0)));
413 }
414}