prometheus_http_query/selector.rs
1use crate::util::*;
2use std::fmt;
3
4/// A time series selector that is gradually built from a metric name and/or
5/// a set of label matchers.
6#[derive(Debug, Clone, PartialEq)]
7pub struct Selector<'a> {
8 pub(crate) labels: Vec<Label<'a>>,
9}
10
11impl<'a> Default for Selector<'a> {
12 fn default() -> Self {
13 Self::new()
14 }
15}
16
17impl<'a> Selector<'a> {
18 /// Create a new instance of [Selector].
19 pub fn new() -> Self {
20 Selector { labels: vec![] }
21 }
22
23 /// Select a metric name for this [Selector].
24 ///
25 /// ```rust
26 /// use prometheus_http_query::Selector;
27 ///
28 /// let select = Selector::new().metric("http_requests_total");
29 ///
30 /// // This is equal to:
31 /// let other_select = Selector::new().eq("__name__", "http_requests_total");
32 ///
33 /// assert_eq!(select, other_select);
34 /// ```
35 pub fn metric(mut self, metric: &'a str) -> Self
36 where
37 Self: Sized,
38 {
39 self.labels.push(Label::Equal(("__name__", metric)));
40 self
41 }
42
43 /// Append a label matcher to the set of matchers of [Selector] that
44 /// selects labels that match the provided string.<br>
45 /// PromQL equivalent: `http_requests_total{job="apiserver"}`
46 ///
47 /// ```rust
48 /// use prometheus_http_query::Selector;
49 ///
50 /// let select = Selector::new()
51 /// .metric("http_requests_total")
52 /// .eq("job", "apiserver")
53 /// .to_string();
54 ///
55 /// let expected = r#"{__name__="http_requests_total",job="apiserver"}"#.to_string();
56 ///
57 /// assert_eq!(select, expected);
58 /// ```
59 pub fn eq(mut self, label: &'a str, value: &'a str) -> Self
60 where
61 Self: Sized,
62 {
63 self.labels.push(Label::Equal((label, value)));
64 self
65 }
66
67 /// Append a label matcher to the set of matchers of [Selector] that
68 /// selects labels that do not match the provided string.<br>
69 /// PromQL equivalent: `http_requests_total{job!="apiserver"}`
70 ///
71 /// ```rust
72 /// use prometheus_http_query::Selector;
73 ///
74 /// let select = Selector::new()
75 /// .metric("http_requests_total")
76 /// .ne("job", "apiserver")
77 /// .to_string();
78 ///
79 /// let expected = r#"{__name__="http_requests_total",job!="apiserver"}"#.to_string();
80 ///
81 /// assert_eq!(select, expected);
82 /// ```
83 pub fn ne(mut self, label: &'a str, value: &'a str) -> Self
84 where
85 Self: Sized,
86 {
87 self.labels.push(Label::NotEqual((label, value)));
88 self
89 }
90
91 /// Append a label matcher to the set of matchers of [Selector] that
92 /// selects labels that regex-match the provided string.
93 /// PromQL equivalent: `http_requests_total{job=~"apiserver"}`
94 ///
95 /// ```rust
96 /// use prometheus_http_query::Selector;
97 ///
98 /// let select = Selector::new()
99 /// .metric("http_requests_total")
100 /// .regex_eq("job", "apiserver")
101 /// .to_string();
102 ///
103 /// let expected = r#"{__name__="http_requests_total",job=~"apiserver"}"#.to_string();
104 ///
105 /// assert_eq!(select, expected);
106 /// ```
107 pub fn regex_eq(mut self, label: &'a str, value: &'a str) -> Self
108 where
109 Self: Sized,
110 {
111 self.labels.push(Label::RegexEqual((label, value)));
112 self
113 }
114
115 /// Append a label matcher to the set of matchers of [Selector] that
116 /// selects labels that do not regex-match the provided string.<br>
117 /// PromQL equivalent: `http_requests_total{job!~"apiserver"}`
118 ///
119 /// ```rust
120 /// use prometheus_http_query::Selector;
121 ///
122 /// let select = Selector::new()
123 /// .metric("http_requests_total")
124 /// .regex_ne("job", "apiserver")
125 /// .to_string();
126 ///
127 /// let expected = r#"{__name__="http_requests_total",job!~"apiserver"}"#.to_string();
128 ///
129 /// assert_eq!(select, expected);
130 /// ```
131 pub fn regex_ne(mut self, label: &'a str, value: &'a str) -> Self
132 where
133 Self: Sized,
134 {
135 self.labels.push(Label::RegexNotEqual((label, value)));
136 self
137 }
138}
139
140impl<'a> fmt::Display for Selector<'a> {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
142 let matchers = self
143 .labels
144 .iter()
145 .map(|l| l.to_string())
146 .collect::<Vec<String>>();
147
148 write!(f, "{{{}}}", matchers.as_slice().join(","))
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155 use crate::util::Label;
156
157 #[test]
158 fn test_selector_display_impl() {
159 let s = Selector {
160 labels: vec![
161 Label::Equal(("__name__", "http_requests_total")),
162 Label::Equal(("handler", "/api/comments")),
163 Label::RegexEqual(("job", ".*server")),
164 Label::RegexNotEqual(("status", "4..")),
165 Label::NotEqual(("env", "test")),
166 ],
167 };
168
169 let result = String::from("{__name__=\"http_requests_total\",handler=\"/api/comments\",job=~\".*server\",status!~\"4..\",env!=\"test\"}");
170
171 assert_eq!(s.to_string(), result);
172 }
173}