prometheus_parser/types/
operator.rs

1// (C) Copyright 2019-2020 Hewlett Packard Enterprise Development LP
2
3use std::fmt;
4
5use super::expression::{BExpression, Expression};
6use super::return_value::{LabelSetOp, ReturnKind, ReturnValue};
7use super::misc::Span;
8
9/// All legal binary operator types
10#[derive(Debug, PartialEq, Eq, Copy, Clone)]
11pub enum OperatorKind {
12  Power,
13  Multiply, Divide, Modulo,
14  Add, Subtract,
15  Equal, NotEqual,
16  LessThan, LessThanEqual,
17  GreaterThan, GreaterThanEqual,
18  And, Unless, Or
19}
20
21impl OperatorKind {
22  pub fn as_str(self) -> &'static str {
23    use OperatorKind::*;
24    match self {
25      Power => "^",
26      Multiply => "*",
27      Divide => "/",
28      Modulo => "%",
29      Add => "+",
30      Subtract => "-",
31      Equal => "==",
32      NotEqual => "!=",
33      LessThan => "<",
34      LessThanEqual => "<=",
35      GreaterThan => ">",
36      GreaterThanEqual => ">=",
37      And => "and",
38      Unless => "unless",
39      Or => "or"
40    }
41  }
42}
43
44impl fmt::Display for OperatorKind {
45  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46    write!(f, "{}", self.as_str())
47  }
48}
49
50/// Matching group clause types (`group_left`, `group_right`)
51#[derive(Debug, PartialEq, Eq, Clone)]
52pub enum MatchingGroupOp {
53  Left,
54  Right
55}
56
57impl fmt::Display for MatchingGroupOp {
58  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59    match self {
60      MatchingGroupOp::Left => write!(f, "group_left"),
61      MatchingGroupOp::Right => write!(f, "group_right")
62    }
63  }
64}
65
66/// A matching clause's nested grouping clause
67#[derive(Debug, PartialEq, Eq, Clone)]
68pub struct MatchingGroup {
69  /// The matching group's operator type (left or right)
70  pub op: MatchingGroupOp,
71
72  /// A list of labels to copy to the opposite side of the group operator, i.e.
73  /// group_left(foo) copies the label `foo` from the right hand side
74  pub labels: Vec<String>,
75
76  pub span: Option<Span>
77}
78
79impl MatchingGroup {
80  pub fn new(op: MatchingGroupOp) -> Self {
81    MatchingGroup {
82      op,
83      labels: vec![],
84      span: None
85    }
86  }
87
88  /// Creates a new MatchingGroup with the Left op
89  pub fn left() -> Self {
90    MatchingGroup::new(MatchingGroupOp::Left)
91  }
92
93  /// Creates a new MatchingGroup with the Right op
94  pub fn right() -> Self {
95    MatchingGroup::new(MatchingGroupOp::Right)
96  }
97
98  /// Replaces this Matching's operator
99  pub fn op(mut self, op: MatchingGroupOp) -> Self {
100    self.op = op;
101    self
102  }
103
104  /// Adds a label key to this MatchingGroup
105  pub fn label<S: Into<String>>(mut self, label: S) -> Self {
106    self.labels.push(label.into());
107    self
108  }
109
110  /// Replaces this MatchingGroup's labels with the given set
111  pub fn labels(mut self, labels: &[&str]) -> Self {
112    self.labels = labels.iter().map(|l| (*l).to_string()).collect();
113    self
114  }
115
116  /// Clears this MatchingGroup's set of labels
117  pub fn clear_labels(mut self) -> Self {
118    self.labels.clear();
119    self
120  }
121
122  pub fn span<S: Into<Span>>(mut self, span: S) -> Self {
123    self.span = Some(span.into());
124    self
125  }
126}
127
128impl fmt::Display for MatchingGroup {
129  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
130    write!(f, "{}", self.op)?;
131
132    if !self.labels.is_empty() {
133      write!(f, "(")?;
134
135      for (i, label) in self.labels.iter().enumerate() {
136        if i > 0 {
137          write!(f, ", ")?;
138        }
139
140        write!(f, "{}", label)?;
141      }
142
143      write!(f, ")")?;
144    }
145
146    Ok(())
147  }
148}
149
150/// A matching clause type
151#[derive(Debug, PartialEq, Eq, Clone)]
152pub enum MatchingOp {
153  On,
154  Ignoring
155}
156
157impl fmt::Display for MatchingOp {
158  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159    match self {
160      MatchingOp::On => write!(f, "on"),
161      MatchingOp::Ignoring => write!(f, "ignoring")
162    }
163  }
164}
165
166/// An operator matching clause
167#[derive(Debug, PartialEq, Eq, Clone)]
168pub struct Matching {
169  pub op: MatchingOp,
170
171  /// A list of labels to which the operator is applied
172  pub labels: Vec<String>,
173
174  /// An optional grouping clause for many-to-one and one-to-many vector matches
175  pub group: Option<MatchingGroup>,
176
177  pub span: Option<Span>
178}
179
180impl Matching {
181  pub fn new(op: MatchingOp) -> Self {
182    Matching {
183      op,
184      labels: vec![],
185      group: None,
186      span: None
187    }
188  }
189
190  /// Creates a Matching cause with the On operator
191  pub fn on() -> Self {
192    Matching::new(MatchingOp::On)
193  }
194
195  /// Creates a Matching clause using the Ignoring operator
196  pub fn ignoring() -> Self {
197    Matching::new(MatchingOp::Ignoring)
198  }
199
200  /// Replaces this Matching's operator
201  pub fn op(mut self, op: MatchingOp) -> Self {
202    self.op = op;
203    self
204  }
205
206  /// Adds a label key to this Matching
207  pub fn label<S: Into<String>>(mut self, label: S) -> Self {
208    self.labels.push(label.into());
209    self
210  }
211
212  /// Replaces this Matching's labels with the given set
213  pub fn labels(mut self, labels: &[&str]) -> Self {
214    self.labels = labels.iter().map(|l| (*l).to_string()).collect();
215    self
216  }
217
218  /// Clears this Matching's set of labels
219  pub fn clear_labels(mut self) -> Self {
220    self.labels.clear();
221    self
222  }
223
224  /// Sets or replaces this Matching's group clause
225  pub fn group(mut self, group: MatchingGroup) -> Self {
226    self.group = Some(group);
227    self
228  }
229
230  /// Clears this Matching's group clause
231  pub fn clear_group(mut self) -> Self {
232    self.group = None;
233    self
234  }
235
236  pub fn span<S: Into<Span>>(mut self, span: S) -> Self {
237    self.span = Some(span.into());
238    self
239  }
240}
241
242impl fmt::Display for Matching {
243  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
244    write!(f, "{}(", self.op)?;
245
246    for (i, label) in self.labels.iter().enumerate() {
247      if i > 0 {
248        write!(f, ", ")?;
249      }
250
251      write!(f, "{}", label)?;
252    }
253
254    write!(f, ")")?;
255
256    if let Some(group) = &self.group {
257      write!(f, " {}", group)?;
258    }
259
260    Ok(())
261  }
262}
263
264/// A binary operator expression with left- and right-hand sides.
265///
266/// Operator expressions may optionally have a matching clause for more specific
267/// vector matching behavior.
268///
269/// Note that operator precedence is accounted for at parse-time, so the
270/// resulting tree (i.e. lhs/rhs expressions) should already account for
271/// un-grouped expressions at the same syntax level.
272#[derive(Debug, PartialEq, Clone)]
273pub struct Operator {
274  /// This Operator's function (multiply, divide, power, equals, etc)
275  pub kind: OperatorKind,
276
277  /// The left-hand-side expression
278  pub lhs: BExpression,
279
280  /// The right-hand-side expression
281  pub rhs: BExpression,
282
283  /// An optional matching clause for this operator (`on(...)`, `ignoring(...)`)
284  pub matching: Option<Matching>,
285
286  pub span: Option<Span>
287}
288
289impl Operator {
290  pub fn new(kind: OperatorKind, lhs: Expression, rhs: Expression) -> Self {
291    Operator {
292      kind,
293      lhs: Box::new(lhs),
294      rhs: Box::new(rhs),
295      matching: None,
296      span: None
297    }
298  }
299
300  /// Sets or replaces this Operator's Matching clause
301  pub fn matching(mut self, matching: Matching) -> Self {
302    self.matching = Some(matching);
303    self
304  }
305
306  /// Clears this Operator's Matching clause, if any
307  pub fn clear_matching(mut self) -> Self {
308    self.matching = None;
309    self
310  }
311
312  pub fn span<S: Into<Span>>(mut self, span: S) -> Self {
313    self.span = Some(span.into());
314    self
315  }
316
317  /// Wraps this Operator in an Expression
318  pub fn wrap(self) -> Expression {
319    Expression::Operator(self)
320  }
321
322  pub fn return_value(&self) -> ReturnValue {
323    // note: largely based on the description from:
324    // https://www.robustperception.io/using-group_left-to-calculate-label-proportions
325
326    // binary operator exprs can only contain (and return) instant vectors
327    let lhs_ret = self.lhs.return_value();
328    let rhs_ret = self.rhs.return_value();
329
330    // operators can only have instant vectors or scalars
331    if !lhs_ret.kind.is_operator_valid() {
332      return ReturnValue::unknown(
333        format!("lhs return type ({:?}) is not valid in an operator", &lhs_ret.kind),
334        self.clone().wrap()
335      );
336    }
337
338    if !rhs_ret.kind.is_operator_valid() {
339      return ReturnValue::unknown(
340        format!("rhs return type ({:?}) is not valid in an operator", &rhs_ret.kind),
341        self.clone().wrap()
342      );
343    }
344
345    let kind;
346    let mut label_ops;
347
348    if lhs_ret.kind.is_scalar() && rhs_ret.kind.is_scalar() {
349      // scalar / scalar = scalar, otherwise instant vector
350      kind = ReturnKind::Scalar;
351      label_ops = vec![LabelSetOp::clear(self.clone().wrap(), self.span)];
352    } else if lhs_ret.kind.is_scalar() {
353      // lhs is scalar, so pull labels from the rhs
354      kind = ReturnKind::InstantVector;
355      label_ops = rhs_ret.label_ops;
356    } else if rhs_ret.kind.is_scalar() {
357      // rhs is scalar, so pull labels from the lhs
358      kind = ReturnKind::InstantVector;
359      label_ops = lhs_ret.label_ops;
360    } else {
361      kind = ReturnKind::InstantVector;
362
363      // neither side is scalar, so unless there's a matching clause with a
364      // group_*, the choice is arbitrary
365      // i.e. expressions without matching label sets will just return an empty
366      // set of metrics
367      
368      // on/ignoring clauses don't affect labels themselves, but a group_* may
369      if let Some(matching) = &self.matching {
370        if let Some(group) = &matching.group {
371          match &group.op {
372            MatchingGroupOp::Left => label_ops = lhs_ret.label_ops,
373            MatchingGroupOp::Right => label_ops = rhs_ret.label_ops
374          };
375
376          // any explicitly-specified labels are copied from the opposite side
377          // we don't care about the value, but it does imply that the label
378          // will exist in the output
379          label_ops.push(LabelSetOp::append(
380            self.clone().wrap(),
381            group.span,
382            group.labels.iter().cloned().collect(),
383          ));
384        } else {
385          label_ops = lhs_ret.label_ops;
386        }
387      } else {
388        label_ops = lhs_ret.label_ops;
389      }
390    };
391
392    ReturnValue { kind, label_ops }
393  }
394}
395
396impl fmt::Display for Operator {
397  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
398    write!(f, "{} {}", self.lhs, self.kind)?;
399
400    if let Some(matching) = &self.matching {
401      write!(f, " {}", matching)?;
402    }
403
404    write!(f, " {}", self.rhs)?;
405
406    Ok(())
407  }
408}