1use std::fmt;
16use std::hash::{Hash, Hasher};
17
18use regex::Regex;
19
20use crate::parser::token::{token_display, TokenId, T_EQL, T_EQL_REGEX, T_NEQ, T_NEQ_REGEX};
21use crate::util::join_vector;
22
23#[derive(Debug, Clone)]
24pub enum MatchOp {
25 Equal,
26 NotEqual,
27 Re(Regex),
28 NotRe(Regex),
29}
30
31impl fmt::Display for MatchOp {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 match self {
34 MatchOp::Equal => write!(f, "="),
35 MatchOp::NotEqual => write!(f, "!="),
36 MatchOp::Re(_reg) => write!(f, "=~"),
37 MatchOp::NotRe(_reg) => write!(f, "!~"),
38 }
39 }
40}
41
42impl PartialEq for MatchOp {
43 fn eq(&self, other: &Self) -> bool {
44 match (self, other) {
45 (MatchOp::Equal, MatchOp::Equal) => true,
46 (MatchOp::NotEqual, MatchOp::NotEqual) => true,
47 (MatchOp::Re(s), MatchOp::Re(o)) => s.as_str().eq(o.as_str()),
48 (MatchOp::NotRe(s), MatchOp::NotRe(o)) => s.as_str().eq(o.as_str()),
49 _ => false,
50 }
51 }
52}
53
54impl Eq for MatchOp {}
55
56impl Hash for MatchOp {
57 fn hash<H: Hasher>(&self, state: &mut H) {
58 match self {
59 MatchOp::Equal => "eq".hash(state),
60 MatchOp::NotEqual => "ne".hash(state),
61 MatchOp::Re(s) => format!("re:{}", s.as_str()).hash(state),
62 MatchOp::NotRe(s) => format!("nre:{}", s.as_str()).hash(state),
63 }
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Hash)]
69pub struct Matcher {
70 pub op: MatchOp,
71 pub name: String,
72 pub value: String,
73 pub is_or: bool,
74}
75
76impl Matcher {
77 pub fn new(op: MatchOp, name: &str, value: &str) -> Self {
78 Self {
79 op,
80 name: name.into(),
81 value: value.into(),
82 is_or: false,
83 }
84 }
85
86 pub fn new_or(op: MatchOp, name: &str, value: &str) -> Self {
87 Self {
88 op,
89 name: name.into(),
90 value: value.into(),
91 is_or: true,
92 }
93 }
94
95 pub fn is_match(&self, s: &str) -> bool {
97 match &self.op {
98 MatchOp::Equal => self.value.eq(s),
99 MatchOp::NotEqual => self.value.ne(s),
100 MatchOp::Re(r) => r.is_match(s),
101 MatchOp::NotRe(r) => !r.is_match(s),
102 }
103 }
104
105 fn try_parse_re(re: &str) -> Result<Regex, String> {
110 Regex::new(re)
111 .or_else(|_| Regex::new(&try_escape_for_repeat_re(re)))
112 .map_err(|_| format!("illegal regex for {re}",))
113 }
114
115 pub fn new_matcher(id: TokenId, name: String, value: String) -> Result<Matcher, String> {
116 let op = Self::find_matcher_op(id, &value)?;
117 op.map(|op| Matcher::new(op, name.as_str(), value.as_str()))
118 }
119
120 pub fn new_matcher_or(id: TokenId, name: String, value: String) -> Result<Matcher, String> {
121 let op = Self::find_matcher_op(id, &value)?;
122 op.map(|op| Matcher::new_or(op, name.as_str(), value.as_str()))
123 }
124
125 fn find_matcher_op(id: TokenId, value: &str) -> Result<Result<MatchOp, String>, String> {
126 let op = match id {
127 T_EQL => Ok(MatchOp::Equal),
128 T_NEQ => Ok(MatchOp::NotEqual),
129 T_EQL_REGEX => Ok(MatchOp::Re(Matcher::try_parse_re(value)?)),
130 T_NEQ_REGEX => Ok(MatchOp::NotRe(Matcher::try_parse_re(value)?)),
131 _ => Err(format!("invalid match op {}", token_display(id))),
132 };
133 Ok(op)
134 }
135}
136
137impl fmt::Display for Matcher {
138 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139 write!(f, "{}{}\"{}\"", self.name, self.op, self.value)
140 }
141}
142
143fn try_escape_for_repeat_re(re: &str) -> String {
148 fn is_repeat(chars: &mut std::str::Chars<'_>) -> (bool, String) {
149 let mut buf = String::new();
150 let mut comma_seen = false;
151 for c in chars.by_ref() {
152 buf.push(c);
153 match c {
154 ',' if comma_seen => {
155 return (false, buf); }
157 ',' if buf == "," => {
158 return (false, buf); }
160 ',' if !comma_seen => comma_seen = true,
161 '}' if buf == "}" => {
162 return (false, buf); }
164 '}' => {
165 return (true, buf);
166 }
167 _ if c.is_ascii_digit() => continue,
168 _ => {
169 return (false, buf); }
171 }
172 }
173 (false, buf) }
175
176 let mut result = String::with_capacity(re.len() + 1);
177 let mut chars = re.chars();
178
179 while let Some(c) = chars.next() {
180 match c {
181 '\\' => {
182 if let Some(cc) = chars.next() {
183 result.push(c);
184 result.push(cc);
185 }
186 }
187 '{' => {
188 let (is, s) = is_repeat(&mut chars);
189 if !is {
190 result.push('\\');
191 }
192 result.push(c);
193 result.push_str(&s);
194 }
195 _ => result.push(c),
196 }
197 }
198 result
199}
200
201#[derive(Debug, Clone, PartialEq, Eq)]
202pub struct Matchers {
203 pub matchers: Vec<Matcher>,
204}
205
206impl Matchers {
207 pub fn empty() -> Self {
208 Self { matchers: vec![] }
209 }
210
211 pub fn one(matcher: Matcher) -> Self {
212 let matchers = vec![matcher];
213 Self { matchers }
214 }
215
216 pub fn new(matchers: Vec<Matcher>) -> Self {
217 Self { matchers }
218 }
219
220 pub fn append(mut self, matcher: Matcher) -> Self {
221 self.matchers.push(matcher);
222 self
223 }
224
225 pub fn is_empty_matchers(&self) -> bool {
231 self.matchers.is_empty() || self.matchers.iter().all(|m| m.is_match(""))
232 }
233
234 pub(crate) fn find_matcher_value(&self, name: &str) -> Option<String> {
237 for m in &self.matchers {
238 if m.name.eq(name) {
239 return Some(m.value.clone());
240 }
241 }
242 None
243 }
244
245 pub fn find_matchers(&self, name: &str) -> Vec<Matcher> {
247 self.matchers
248 .iter()
249 .filter(|m| m.name.eq(name))
250 .cloned()
251 .collect()
252 }
253}
254
255impl fmt::Display for Matchers {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 let not_contains_or = &self.matchers.iter().all(|matcher| !matcher.is_or);
258 if *not_contains_or {
259 write!(f, "{}", join_vector(&self.matchers, ",", true))
260 } else {
261 let matchers_str = self
262 .matchers
263 .iter()
264 .map(|matcher| {
265 if matcher.is_or {
266 format!(" or {}", matcher)
267 } else {
268 format!(",{}", matcher)
269 }
270 })
271 .collect::<String>();
272 write!(f, "{}", matchers_str.trim_start_matches(','))
273 }
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280 use crate::parser::token;
281 use std::collections::hash_map::DefaultHasher;
282
283 fn hash<H>(op: H) -> u64
284 where
285 H: Hash,
286 {
287 let mut hasher = DefaultHasher::new();
288 op.hash(&mut hasher);
289 hasher.finish()
290 }
291
292 #[test]
293 fn test_new_matcher() {
294 assert_eq!(
295 Matcher::new_matcher(token::T_ADD, "".into(), "".into()),
296 Err(format!("invalid match op {}", token_display(token::T_ADD)))
297 )
298 }
299
300 #[test]
301 fn test_matcher_op_eq() {
302 assert_eq!(MatchOp::Equal, MatchOp::Equal);
303 assert_eq!(MatchOp::NotEqual, MatchOp::NotEqual);
304 assert_eq!(
305 MatchOp::Re(Regex::new("\\s+").unwrap()),
306 MatchOp::Re(Regex::new("\\s+").unwrap())
307 );
308 assert_eq!(
309 MatchOp::NotRe(Regex::new("\\s+").unwrap()),
310 MatchOp::NotRe(Regex::new("\\s+").unwrap())
311 );
312
313 assert_ne!(MatchOp::Equal, MatchOp::NotEqual);
314 assert_ne!(
315 MatchOp::NotEqual,
316 MatchOp::NotRe(Regex::new("\\s+").unwrap())
317 );
318 assert_ne!(
319 MatchOp::Re(Regex::new("\\s+").unwrap()),
320 MatchOp::NotRe(Regex::new("\\s+").unwrap())
321 );
322 }
323
324 #[test]
325 fn test_matchop_hash() {
326 assert_eq!(hash(MatchOp::Equal), hash(MatchOp::Equal));
327 assert_eq!(hash(MatchOp::NotEqual), hash(MatchOp::NotEqual));
328 assert_eq!(
329 hash(MatchOp::Re(Regex::new("\\s+").unwrap())),
330 hash(MatchOp::Re(Regex::new("\\s+").unwrap()))
331 );
332 assert_eq!(
333 hash(MatchOp::NotRe(Regex::new("\\s+").unwrap())),
334 hash(MatchOp::NotRe(Regex::new("\\s+").unwrap()))
335 );
336
337 assert_ne!(hash(MatchOp::Equal), hash(MatchOp::NotEqual));
338 assert_ne!(
339 hash(MatchOp::NotEqual),
340 hash(MatchOp::NotRe(Regex::new("\\s+").unwrap()))
341 );
342 assert_ne!(
343 hash(MatchOp::Re(Regex::new("\\s+").unwrap())),
344 hash(MatchOp::NotRe(Regex::new("\\s+").unwrap()))
345 );
346 }
347
348 #[test]
349 fn test_matcher_hash() {
350 assert_eq!(
351 hash(Matcher::new(MatchOp::Equal, "name", "value")),
352 hash(Matcher::new(MatchOp::Equal, "name", "value")),
353 );
354
355 assert_eq!(
356 hash(Matcher::new(MatchOp::NotEqual, "name", "value")),
357 hash(Matcher::new(MatchOp::NotEqual, "name", "value")),
358 );
359
360 assert_eq!(
361 hash(Matcher::new(
362 MatchOp::Re(Regex::new("\\s+").unwrap()),
363 "name",
364 "\\s+"
365 )),
366 hash(Matcher::new(
367 MatchOp::Re(Regex::new("\\s+").unwrap()),
368 "name",
369 "\\s+"
370 )),
371 );
372
373 assert_eq!(
374 hash(Matcher::new(
375 MatchOp::NotRe(Regex::new("\\s+").unwrap()),
376 "name",
377 "\\s+"
378 )),
379 hash(Matcher::new(
380 MatchOp::NotRe(Regex::new("\\s+").unwrap()),
381 "name",
382 "\\s+"
383 )),
384 );
385
386 assert_ne!(
387 hash(Matcher::new(MatchOp::Equal, "name", "value")),
388 hash(Matcher::new(MatchOp::NotEqual, "name", "value")),
389 );
390
391 assert_ne!(
392 hash(Matcher::new(
393 MatchOp::Re(Regex::new("\\s+").unwrap()),
394 "name",
395 "\\s+"
396 )),
397 hash(Matcher::new(
398 MatchOp::NotRe(Regex::new("\\s+").unwrap()),
399 "name",
400 "\\s+"
401 )),
402 );
403 }
404
405 #[test]
406 fn test_matcher_eq_ne() {
407 let op = MatchOp::Equal;
408 let matcher = Matcher::new(op, "name", "up");
409 assert!(matcher.is_match("up"));
410 assert!(!matcher.is_match("down"));
411
412 let op = MatchOp::NotEqual;
413 let matcher = Matcher::new(op, "name", "up");
414 assert!(matcher.is_match("foo"));
415 assert!(matcher.is_match("bar"));
416 assert!(!matcher.is_match("up"));
417 }
418
419 #[test]
420 fn test_matcher_re() {
421 let value = "api/v1/.*";
422 let re = Regex::new(value).unwrap();
423 let op = MatchOp::Re(re);
424 let matcher = Matcher::new(op, "name", value);
425 assert!(matcher.is_match("api/v1/query"));
426 assert!(matcher.is_match("api/v1/range_query"));
427 assert!(!matcher.is_match("api/v2"));
428 }
429
430 #[test]
431 fn test_eq_matcher_equality() {
432 assert_eq!(
433 Matcher::new(MatchOp::Equal, "code", "200"),
434 Matcher::new(MatchOp::Equal, "code", "200")
435 );
436
437 assert_ne!(
438 Matcher::new(MatchOp::Equal, "code", "200"),
439 Matcher::new(MatchOp::Equal, "code", "201")
440 );
441
442 assert_ne!(
443 Matcher::new(MatchOp::Equal, "code", "200"),
444 Matcher::new(MatchOp::NotEqual, "code", "200")
445 );
446 }
447
448 #[test]
449 fn test_ne_matcher_equality() {
450 assert_eq!(
451 Matcher::new(MatchOp::NotEqual, "code", "200"),
452 Matcher::new(MatchOp::NotEqual, "code", "200")
453 );
454
455 assert_ne!(
456 Matcher::new(MatchOp::NotEqual, "code", "200"),
457 Matcher::new(MatchOp::NotEqual, "code", "201")
458 );
459
460 assert_ne!(
461 Matcher::new(MatchOp::NotEqual, "code", "200"),
462 Matcher::new(MatchOp::Equal, "code", "200")
463 );
464 }
465
466 #[test]
467 fn test_re_matcher_equality() {
468 assert_eq!(
469 Matcher::new(MatchOp::Re(Regex::new("2??").unwrap()), "code", "2??",),
470 Matcher::new(MatchOp::Re(Regex::new("2??").unwrap()), "code", "2??",)
471 );
472
473 assert_ne!(
474 Matcher::new(MatchOp::Re(Regex::new("2??").unwrap()), "code", "2??",),
475 Matcher::new(MatchOp::Re(Regex::new("2??").unwrap()), "code", "2*?",)
476 );
477
478 assert_ne!(
479 Matcher::new(MatchOp::Re(Regex::new("2??").unwrap()), "code", "2??",),
480 Matcher::new(MatchOp::Equal, "code", "2??")
481 );
482 }
483
484 #[test]
485 fn test_not_re_matcher_equality() {
486 assert_eq!(
487 Matcher::new(MatchOp::NotRe(Regex::new("2??").unwrap()), "code", "2??",),
488 Matcher::new(MatchOp::NotRe(Regex::new("2??").unwrap()), "code", "2??",)
489 );
490
491 assert_ne!(
492 Matcher::new(MatchOp::NotRe(Regex::new("2??").unwrap()), "code", "2??",),
493 Matcher::new(MatchOp::NotRe(Regex::new("2?*").unwrap()), "code", "2*?",)
494 );
495
496 assert_ne!(
497 Matcher::new(MatchOp::NotRe(Regex::new("2??").unwrap()), "code", "2??",),
498 Matcher::new(MatchOp::Equal, "code", "2??")
499 );
500 }
501
502 #[test]
503 fn test_matchers_equality() {
504 assert_eq!(
505 Matchers::empty()
506 .append(Matcher::new(MatchOp::Equal, "name1", "val1"))
507 .append(Matcher::new(MatchOp::Equal, "name2", "val2")),
508 Matchers::empty()
509 .append(Matcher::new(MatchOp::Equal, "name1", "val1"))
510 .append(Matcher::new(MatchOp::Equal, "name2", "val2"))
511 );
512
513 assert_ne!(
514 Matchers::empty().append(Matcher::new(MatchOp::Equal, "name1", "val1")),
515 Matchers::empty().append(Matcher::new(MatchOp::Equal, "name2", "val2"))
516 );
517
518 assert_ne!(
519 Matchers::empty().append(Matcher::new(MatchOp::Equal, "name1", "val1")),
520 Matchers::empty().append(Matcher::new(MatchOp::NotEqual, "name1", "val1"))
521 );
522
523 assert_eq!(
524 Matchers::empty()
525 .append(Matcher::new(MatchOp::Equal, "name1", "val1"))
526 .append(Matcher::new(MatchOp::NotEqual, "name2", "val2"))
527 .append(Matcher::new(
528 MatchOp::Re(Regex::new("\\d+").unwrap()),
529 "name2",
530 "\\d+"
531 ))
532 .append(Matcher::new(
533 MatchOp::NotRe(Regex::new("\\d+").unwrap()),
534 "name2",
535 "\\d+"
536 )),
537 Matchers::empty()
538 .append(Matcher::new(MatchOp::Equal, "name1", "val1"))
539 .append(Matcher::new(MatchOp::NotEqual, "name2", "val2"))
540 .append(Matcher::new(
541 MatchOp::Re(Regex::new("\\d+").unwrap()),
542 "name2",
543 "\\d+"
544 ))
545 .append(Matcher::new(
546 MatchOp::NotRe(Regex::new("\\d+").unwrap()),
547 "name2",
548 "\\d+"
549 ))
550 );
551 }
552
553 #[test]
554 fn test_find_matchers() {
555 let matchers = Matchers::empty()
556 .append(Matcher::new(MatchOp::Equal, "foo", "bar"))
557 .append(Matcher::new(MatchOp::NotEqual, "foo", "bar"))
558 .append(Matcher::new_matcher(T_EQL_REGEX, "foo".into(), "bar".into()).unwrap())
559 .append(Matcher::new_matcher(T_NEQ_REGEX, "foo".into(), "bar".into()).unwrap())
560 .append(Matcher::new(MatchOp::Equal, "FOO", "bar"))
561 .append(Matcher::new(MatchOp::NotEqual, "bar", "bar"));
562
563 let ms = matchers.find_matchers("foo");
564 assert_eq!(4, ms.len());
565 }
566
567 #[test]
568 fn test_convert_re() {
569 assert_eq!(try_escape_for_repeat_re("abc{}"), r"abc\{}");
570 assert_eq!(try_escape_for_repeat_re("abc{def}"), r"abc\{def}");
571 assert_eq!(try_escape_for_repeat_re("abc{def"), r"abc\{def");
572 assert_eq!(try_escape_for_repeat_re("abc{1}"), "abc{1}");
573 assert_eq!(try_escape_for_repeat_re("abc{1,}"), "abc{1,}");
574 assert_eq!(try_escape_for_repeat_re("abc{1,2}"), "abc{1,2}");
575 assert_eq!(try_escape_for_repeat_re("abc{,2}"), r"abc\{,2}");
576 assert_eq!(try_escape_for_repeat_re("abc{{1,2}}"), r"abc\{{1,2}}");
577 assert_eq!(try_escape_for_repeat_re(r"abc\{abc"), r"abc\{abc");
578 assert_eq!(try_escape_for_repeat_re("abc{1a}"), r"abc\{1a}");
579 assert_eq!(try_escape_for_repeat_re("abc{1,a}"), r"abc\{1,a}");
580 assert_eq!(try_escape_for_repeat_re("abc{1,2a}"), r"abc\{1,2a}");
581 assert_eq!(try_escape_for_repeat_re("abc{1,2,3}"), r"abc\{1,2,3}");
582 assert_eq!(try_escape_for_repeat_re("abc{1,,2}"), r"abc\{1,,2}");
583 }
584}