simple_test/label/
matcher.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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// Matcher models the matching of a label.
68#[derive(Debug, Clone, PartialEq, Eq, Hash)]
69pub struct Matcher {
70    pub op: MatchOp,
71    pub name: String,
72    pub value: String,
73}
74
75impl Matcher {
76    pub fn new(op: MatchOp, name: &str, value: &str) -> Self {
77        Self {
78            op,
79            name: name.into(),
80            value: value.into(),
81        }
82    }
83
84    /// matches returns whether the matcher matches the given string value.
85    pub fn is_match(&self, s: &str) -> bool {
86        match &self.op {
87            MatchOp::Equal => self.value.eq(s),
88            MatchOp::NotEqual => self.value.ne(s),
89            MatchOp::Re(r) => r.is_match(s),
90            MatchOp::NotRe(r) => !r.is_match(s),
91        }
92    }
93
94    // Go and Rust handle the repeat pattern differently
95    // in Go the following is valid: `aaa{bbb}ccc`
96    // in Rust {bbb} is seen as an invalid repeat and must be ecaped \{bbb}
97    // This escapes the opening { if its not followed by valid repeat pattern (e.g. 4,6).
98    fn try_parse_re(re: &str) -> Result<Regex, String> {
99        Regex::new(re)
100            .or_else(|_| Regex::new(&try_escape_for_repeat_re(re)))
101            .map_err(|_| format!("illegal regex for {re}",))
102    }
103
104    pub fn new_matcher(id: TokenId, name: String, value: String) -> Result<Matcher, String> {
105        let op = match id {
106            T_EQL => Ok(MatchOp::Equal),
107            T_NEQ => Ok(MatchOp::NotEqual),
108            T_EQL_REGEX => Ok(MatchOp::Re(Matcher::try_parse_re(&value)?)),
109            T_NEQ_REGEX => Ok(MatchOp::NotRe(Matcher::try_parse_re(&value)?)),
110            _ => Err(format!("invalid match op {}", token_display(id))),
111        };
112
113        op.map(|op| Matcher { op, name, value })
114    }
115}
116
117impl fmt::Display for Matcher {
118    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119        write!(f, "{}{}\"{}\"", self.name, self.op, self.value)
120    }
121}
122
123// Go and Rust handle the repeat pattern differently
124// in Go the following is valid: `aaa{bbb}ccc`
125// in Rust {bbb} is seen as an invalid repeat and must be ecaped \{bbb}
126// This escapes the opening { if its not followed by valid repeat pattern (e.g. 4,6).
127fn try_escape_for_repeat_re(re: &str) -> String {
128    fn is_repeat(chars: &mut std::str::Chars<'_>) -> (bool, String) {
129        let mut buf = String::new();
130        let mut comma_seen = false;
131        for c in chars.by_ref() {
132            buf.push(c);
133            match c {
134                ',' if comma_seen => {
135                    return (false, buf); // ,, is invalid
136                }
137                ',' if buf == "," => {
138                    return (false, buf); // {, is invalid
139                }
140                ',' if !comma_seen => comma_seen = true,
141                '}' if buf == "}" => {
142                    return (false, buf); // {} is invalid
143                }
144                '}' => {
145                    return (true, buf);
146                }
147                _ if c.is_ascii_digit() => continue,
148                _ => {
149                    return (false, buf); // false if visit non-digit char
150                }
151            }
152        }
153        (false, buf) // not ended with }
154    }
155
156    let mut result = String::with_capacity(re.len() + 1);
157    let mut chars = re.chars();
158
159    while let Some(c) = chars.next() {
160        match c {
161            '\\' => {
162                if let Some(cc) = chars.next() {
163                    result.push(c);
164                    result.push(cc);
165                }
166            }
167            '{' => {
168                let (is, s) = is_repeat(&mut chars);
169                if !is {
170                    result.push('\\');
171                }
172                result.push(c);
173                result.push_str(&s);
174            }
175            _ => result.push(c),
176        }
177    }
178    result
179}
180
181#[derive(Debug, Clone, PartialEq, Eq)]
182pub struct Matchers {
183    pub matchers: Vec<Matcher>,
184}
185
186impl Matchers {
187    pub fn empty() -> Self {
188        Self { matchers: vec![] }
189    }
190
191    pub fn one(matcher: Matcher) -> Self {
192        let matchers = vec![matcher];
193        Self { matchers }
194    }
195
196    pub fn new(matchers: Vec<Matcher>) -> Self {
197        Self { matchers }
198    }
199
200    pub fn append(mut self, matcher: Matcher) -> Self {
201        self.matchers.push(matcher);
202        self
203    }
204
205    /// Vector selectors must either specify a name or at least one label
206    /// matcher that does not match the empty string.
207    ///
208    /// The following expression is illegal:
209    /// {job=~".*"} # Bad!
210    pub fn is_empty_matchers(&self) -> bool {
211        self.matchers.is_empty() || self.matchers.iter().all(|m| m.is_match(""))
212    }
213
214    /// find the matcher's value whose name equals the specified name. This function
215    /// is designed to prepare error message of invalid promql expression.
216    pub(crate) fn find_matcher_value(&self, name: &str) -> Option<String> {
217        for m in &self.matchers {
218            if m.name.eq(name) {
219                return Some(m.value.clone());
220            }
221        }
222        None
223    }
224
225    /// find matchers whose name equals the specified name
226    pub fn find_matchers(&self, name: &str) -> Vec<Matcher> {
227        self.matchers
228            .iter()
229            .filter(|m| m.name.eq(name))
230            .cloned()
231            .collect()
232    }
233}
234
235impl fmt::Display for Matchers {
236    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237        write!(f, "{}", join_vector(&self.matchers, ",", true))
238    }
239}
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244    use crate::parser::token;
245    use std::collections::hash_map::DefaultHasher;
246
247    fn hash<H>(op: H) -> u64
248    where
249        H: Hash,
250    {
251        let mut hasher = DefaultHasher::new();
252        op.hash(&mut hasher);
253        hasher.finish()
254    }
255
256    #[test]
257    fn test_new_matcher() {
258        assert_eq!(
259            Matcher::new_matcher(token::T_ADD, "".into(), "".into()),
260            Err(format!("invalid match op {}", token_display(token::T_ADD)))
261        )
262    }
263
264    #[test]
265    fn test_matcher_op_eq() {
266        assert_eq!(MatchOp::Equal, MatchOp::Equal);
267        assert_eq!(MatchOp::NotEqual, MatchOp::NotEqual);
268        assert_eq!(
269            MatchOp::Re(Regex::new("\\s+").unwrap()),
270            MatchOp::Re(Regex::new("\\s+").unwrap())
271        );
272        assert_eq!(
273            MatchOp::NotRe(Regex::new("\\s+").unwrap()),
274            MatchOp::NotRe(Regex::new("\\s+").unwrap())
275        );
276
277        assert_ne!(MatchOp::Equal, MatchOp::NotEqual);
278        assert_ne!(
279            MatchOp::NotEqual,
280            MatchOp::NotRe(Regex::new("\\s+").unwrap())
281        );
282        assert_ne!(
283            MatchOp::Re(Regex::new("\\s+").unwrap()),
284            MatchOp::NotRe(Regex::new("\\s+").unwrap())
285        );
286    }
287
288    #[test]
289    fn test_matchop_hash() {
290        assert_eq!(hash(MatchOp::Equal), hash(MatchOp::Equal));
291        assert_eq!(hash(MatchOp::NotEqual), hash(MatchOp::NotEqual));
292        assert_eq!(
293            hash(MatchOp::Re(Regex::new("\\s+").unwrap())),
294            hash(MatchOp::Re(Regex::new("\\s+").unwrap()))
295        );
296        assert_eq!(
297            hash(MatchOp::NotRe(Regex::new("\\s+").unwrap())),
298            hash(MatchOp::NotRe(Regex::new("\\s+").unwrap()))
299        );
300
301        assert_ne!(hash(MatchOp::Equal), hash(MatchOp::NotEqual));
302        assert_ne!(
303            hash(MatchOp::NotEqual),
304            hash(MatchOp::NotRe(Regex::new("\\s+").unwrap()))
305        );
306        assert_ne!(
307            hash(MatchOp::Re(Regex::new("\\s+").unwrap())),
308            hash(MatchOp::NotRe(Regex::new("\\s+").unwrap()))
309        );
310    }
311
312    #[test]
313    fn test_matcher_hash() {
314        assert_eq!(
315            hash(Matcher::new(MatchOp::Equal, "name", "value")),
316            hash(Matcher::new(MatchOp::Equal, "name", "value")),
317        );
318
319        assert_eq!(
320            hash(Matcher::new(MatchOp::NotEqual, "name", "value")),
321            hash(Matcher::new(MatchOp::NotEqual, "name", "value")),
322        );
323
324        assert_eq!(
325            hash(Matcher::new(
326                MatchOp::Re(Regex::new("\\s+").unwrap()),
327                "name",
328                "\\s+"
329            )),
330            hash(Matcher::new(
331                MatchOp::Re(Regex::new("\\s+").unwrap()),
332                "name",
333                "\\s+"
334            )),
335        );
336
337        assert_eq!(
338            hash(Matcher::new(
339                MatchOp::NotRe(Regex::new("\\s+").unwrap()),
340                "name",
341                "\\s+"
342            )),
343            hash(Matcher::new(
344                MatchOp::NotRe(Regex::new("\\s+").unwrap()),
345                "name",
346                "\\s+"
347            )),
348        );
349
350        assert_ne!(
351            hash(Matcher::new(MatchOp::Equal, "name", "value")),
352            hash(Matcher::new(MatchOp::NotEqual, "name", "value")),
353        );
354
355        assert_ne!(
356            hash(Matcher::new(
357                MatchOp::Re(Regex::new("\\s+").unwrap()),
358                "name",
359                "\\s+"
360            )),
361            hash(Matcher::new(
362                MatchOp::NotRe(Regex::new("\\s+").unwrap()),
363                "name",
364                "\\s+"
365            )),
366        );
367    }
368
369    #[test]
370    fn test_matcher_eq_ne() {
371        let op = MatchOp::Equal;
372        let matcher = Matcher::new(op, "name", "up");
373        assert!(matcher.is_match("up"));
374        assert!(!matcher.is_match("down"));
375
376        let op = MatchOp::NotEqual;
377        let matcher = Matcher::new(op, "name", "up");
378        assert!(matcher.is_match("foo"));
379        assert!(matcher.is_match("bar"));
380        assert!(!matcher.is_match("up"));
381    }
382
383    #[test]
384    fn test_matcher_re() {
385        let value = "api/v1/.*";
386        let re = Regex::new(value).unwrap();
387        let op = MatchOp::Re(re);
388        let matcher = Matcher::new(op, "name", value);
389        assert!(matcher.is_match("api/v1/query"));
390        assert!(matcher.is_match("api/v1/range_query"));
391        assert!(!matcher.is_match("api/v2"));
392    }
393
394    #[test]
395    fn test_eq_matcher_equality() {
396        assert_eq!(
397            Matcher::new(MatchOp::Equal, "code", "200"),
398            Matcher::new(MatchOp::Equal, "code", "200")
399        );
400
401        assert_ne!(
402            Matcher::new(MatchOp::Equal, "code", "200"),
403            Matcher::new(MatchOp::Equal, "code", "201")
404        );
405
406        assert_ne!(
407            Matcher::new(MatchOp::Equal, "code", "200"),
408            Matcher::new(MatchOp::NotEqual, "code", "200")
409        );
410    }
411
412    #[test]
413    fn test_ne_matcher_equality() {
414        assert_eq!(
415            Matcher::new(MatchOp::NotEqual, "code", "200"),
416            Matcher::new(MatchOp::NotEqual, "code", "200")
417        );
418
419        assert_ne!(
420            Matcher::new(MatchOp::NotEqual, "code", "200"),
421            Matcher::new(MatchOp::NotEqual, "code", "201")
422        );
423
424        assert_ne!(
425            Matcher::new(MatchOp::NotEqual, "code", "200"),
426            Matcher::new(MatchOp::Equal, "code", "200")
427        );
428    }
429
430    #[test]
431    fn test_re_matcher_equality() {
432        assert_eq!(
433            Matcher::new(MatchOp::Re(Regex::new("2??").unwrap()), "code", "2??",),
434            Matcher::new(MatchOp::Re(Regex::new("2??").unwrap()), "code", "2??",)
435        );
436
437        assert_ne!(
438            Matcher::new(MatchOp::Re(Regex::new("2??").unwrap()), "code", "2??",),
439            Matcher::new(MatchOp::Re(Regex::new("2??").unwrap()), "code", "2*?",)
440        );
441
442        assert_ne!(
443            Matcher::new(MatchOp::Re(Regex::new("2??").unwrap()), "code", "2??",),
444            Matcher::new(MatchOp::Equal, "code", "2??")
445        );
446    }
447
448    #[test]
449    fn test_not_re_matcher_equality() {
450        assert_eq!(
451            Matcher::new(MatchOp::NotRe(Regex::new("2??").unwrap()), "code", "2??",),
452            Matcher::new(MatchOp::NotRe(Regex::new("2??").unwrap()), "code", "2??",)
453        );
454
455        assert_ne!(
456            Matcher::new(MatchOp::NotRe(Regex::new("2??").unwrap()), "code", "2??",),
457            Matcher::new(MatchOp::NotRe(Regex::new("2?*").unwrap()), "code", "2*?",)
458        );
459
460        assert_ne!(
461            Matcher::new(MatchOp::NotRe(Regex::new("2??").unwrap()), "code", "2??",),
462            Matcher::new(MatchOp::Equal, "code", "2??")
463        );
464    }
465
466    #[test]
467    fn test_matchers_equality() {
468        assert_eq!(
469            Matchers::empty()
470                .append(Matcher::new(MatchOp::Equal, "name1", "val1"))
471                .append(Matcher::new(MatchOp::Equal, "name2", "val2")),
472            Matchers::empty()
473                .append(Matcher::new(MatchOp::Equal, "name1", "val1"))
474                .append(Matcher::new(MatchOp::Equal, "name2", "val2"))
475        );
476
477        assert_ne!(
478            Matchers::empty().append(Matcher::new(MatchOp::Equal, "name1", "val1")),
479            Matchers::empty().append(Matcher::new(MatchOp::Equal, "name2", "val2"))
480        );
481
482        assert_ne!(
483            Matchers::empty().append(Matcher::new(MatchOp::Equal, "name1", "val1")),
484            Matchers::empty().append(Matcher::new(MatchOp::NotEqual, "name1", "val1"))
485        );
486
487        assert_eq!(
488            Matchers::empty()
489                .append(Matcher::new(MatchOp::Equal, "name1", "val1"))
490                .append(Matcher::new(MatchOp::NotEqual, "name2", "val2"))
491                .append(Matcher::new(
492                    MatchOp::Re(Regex::new("\\d+").unwrap()),
493                    "name2",
494                    "\\d+"
495                ))
496                .append(Matcher::new(
497                    MatchOp::NotRe(Regex::new("\\d+").unwrap()),
498                    "name2",
499                    "\\d+"
500                )),
501            Matchers::empty()
502                .append(Matcher::new(MatchOp::Equal, "name1", "val1"))
503                .append(Matcher::new(MatchOp::NotEqual, "name2", "val2"))
504                .append(Matcher::new(
505                    MatchOp::Re(Regex::new("\\d+").unwrap()),
506                    "name2",
507                    "\\d+"
508                ))
509                .append(Matcher::new(
510                    MatchOp::NotRe(Regex::new("\\d+").unwrap()),
511                    "name2",
512                    "\\d+"
513                ))
514        );
515    }
516
517    #[test]
518    fn test_find_matchers() {
519        let matchers = Matchers::empty()
520            .append(Matcher::new(MatchOp::Equal, "foo", "bar"))
521            .append(Matcher::new(MatchOp::NotEqual, "foo", "bar"))
522            .append(Matcher::new_matcher(T_EQL_REGEX, "foo".into(), "bar".into()).unwrap())
523            .append(Matcher::new_matcher(T_NEQ_REGEX, "foo".into(), "bar".into()).unwrap())
524            .append(Matcher::new(MatchOp::Equal, "FOO", "bar"))
525            .append(Matcher::new(MatchOp::NotEqual, "bar", "bar"));
526
527        let ms = matchers.find_matchers("foo");
528        assert_eq!(4, ms.len());
529    }
530
531    #[test]
532    fn test_convert_re() {
533        assert_eq!(try_escape_for_repeat_re("abc{}"), r"abc\{}");
534        assert_eq!(try_escape_for_repeat_re("abc{def}"), r"abc\{def}");
535        assert_eq!(try_escape_for_repeat_re("abc{def"), r"abc\{def");
536        assert_eq!(try_escape_for_repeat_re("abc{1}"), "abc{1}");
537        assert_eq!(try_escape_for_repeat_re("abc{1,}"), "abc{1,}");
538        assert_eq!(try_escape_for_repeat_re("abc{1,2}"), "abc{1,2}");
539        assert_eq!(try_escape_for_repeat_re("abc{,2}"), r"abc\{,2}");
540        assert_eq!(try_escape_for_repeat_re("abc{{1,2}}"), r"abc\{{1,2}}");
541        assert_eq!(try_escape_for_repeat_re(r"abc\{abc"), r"abc\{abc");
542        assert_eq!(try_escape_for_repeat_re("abc{1a}"), r"abc\{1a}");
543        assert_eq!(try_escape_for_repeat_re("abc{1,a}"), r"abc\{1,a}");
544        assert_eq!(try_escape_for_repeat_re("abc{1,2a}"), r"abc\{1,2a}");
545        assert_eq!(try_escape_for_repeat_re("abc{1,2,3}"), r"abc\{1,2,3}");
546        assert_eq!(try_escape_for_repeat_re("abc{1,,2}"), r"abc\{1,,2}");
547    }
548}