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