promql_parser/label/
mod.rs1use 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
26pub const METRIC_NAME: &str = "__name__";
28pub const ALERT_NAME: &str = "alertname";
30pub const BUCKET_LABEL: &str = "le";
32pub 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}