promql_parser/label/
mod.rs1use std::collections::HashSet;
18use std::fmt;
19
20use crate::parser::lex::is_label;
21
22mod matcher;
23pub use matcher::{MatchOp, Matcher, Matchers};
24
25pub const METRIC_NAME: &str = "__name__";
27pub const ALERT_NAME: &str = "alertname";
29pub const BUCKET_LABEL: &str = "le";
31pub 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}