Skip to main content

promql_parser/label/
mod.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
15//! Label matchers and Well-known label names used by Prometheus components.
16
17use std::collections::HashSet;
18use std::fmt;
19
20use crate::parser::lex::is_label;
21use crate::util::escape_string;
22
23mod matcher;
24pub use matcher::{MatchOp, Matcher, Matchers};
25
26/// "__name__"
27pub const METRIC_NAME: &str = "__name__";
28/// "alertname"
29pub const ALERT_NAME: &str = "alertname";
30/// "le"
31pub const BUCKET_LABEL: &str = "le";
32/// "instance"
33pub const INSTANCE_NAME: &str = "instance";
34
35pub type Label = String;
36
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct Labels {
39    pub labels: Vec<Label>,
40}
41
42impl Labels {
43    pub fn append(mut self, l: Label) -> Self {
44        self.labels.push(l);
45        self
46    }
47
48    pub fn new(ls: Vec<&str>) -> Self {
49        let labels = ls.iter().map(|s| s.to_string()).collect();
50        Self { labels }
51    }
52
53    pub fn is_empty(&self) -> bool {
54        self.labels.is_empty()
55    }
56
57    pub fn is_joint(&self, ls: &Labels) -> bool {
58        let s1: HashSet<&String> = self.labels.iter().collect();
59        let s2: HashSet<&String> = ls.labels.iter().collect();
60
61        !s1.is_disjoint(&s2)
62    }
63
64    pub fn intersect(&self, ls: &Labels) -> Labels {
65        let s1: HashSet<&String> = self.labels.iter().collect();
66        let s2: HashSet<&String> = ls.labels.iter().collect();
67        let labels = s1.intersection(&s2).map(|s| s.to_string()).collect();
68
69        Self { labels }
70    }
71}
72
73impl fmt::Display for Labels {
74    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75        let formatted_labels: Vec<String> = self
76            .labels
77            .iter()
78            .map(|label| {
79                if is_label(label) {
80                    label.clone()
81                } else {
82                    format!("\"{}\"", escape_string(label))
83                }
84            })
85            .collect();
86        write!(f, "{}", formatted_labels.join(", "))
87    }
88}
89
90#[cfg(feature = "ser")]
91impl serde::Serialize for Labels {
92    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
93    where
94        S: serde::Serializer,
95    {
96        use serde::ser::SerializeSeq;
97        let mut seq = serializer.serialize_seq(Some(self.labels.len()))?;
98
99        for l in &self.labels {
100            seq.serialize_element(&l)?;
101        }
102
103        seq.end()
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn test_to_string() {
113        let cases = vec![
114            (vec![], ""),
115            (vec!["foo"], "foo"),
116            (vec!["foo", "bar"], "foo, bar"),
117            (vec!["foo", "foo", "bar"], "foo, foo, bar"),
118        ];
119
120        for (ls, expect) in cases {
121            let lb = Labels::new(ls);
122            assert_eq!(expect, lb.to_string())
123        }
124    }
125
126    #[test]
127    fn test_is_joint() {
128        let cases = vec![
129            (vec!["foo"], vec!["bar"], false),
130            (vec!["foo"], vec!["foo", "bar"], true),
131            (vec!["foo"], vec!["foo"], true),
132        ];
133
134        for (lb1, lb2, is) in cases {
135            let lb1 = Labels::new(lb1);
136            let lb2 = Labels::new(lb2);
137            assert_eq!(is, lb1.is_joint(&lb2), "{lb1:?} and {lb2:?}")
138        }
139    }
140
141    #[test]
142    fn test_intersect() {
143        let cases = vec![
144            (vec!["foo"], vec!["bar"], vec![]),
145            (vec!["foo"], vec!["foo", "bar"], vec!["foo"]),
146            (vec!["foo"], vec!["foo"], vec!["foo"]),
147            (vec!["foo", "bar"], vec!["bar", "foo"], vec!["foo", "bar"]),
148        ];
149
150        for (lb1, lb2, common) in cases {
151            let lb1 = Labels::new(lb1);
152            let lb2 = Labels::new(lb2);
153            let intersection: HashSet<_> = lb1.intersect(&lb2).labels.into_iter().collect();
154            let expect: HashSet<_> = common.iter().map(|s| s.to_string()).collect();
155            assert_eq!(expect, intersection)
156        }
157    }
158}