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}