1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// (C) Copyright 2019-2020 Hewlett Packard Enterprise Development LP

use std::fmt;

use super::function::Function;
use super::group::Group;
use super::operator::Operator;
use super::return_value::{LabelSetOp, ReturnKind, ReturnValue};
use super::selector::Selector;

/// A root expression node.
///
/// These are all valid root expression types.
#[derive(PartialEq, Clone)]
pub enum Expression {
  /// A single scalar float
  Float(f64),

  /// A single scalar string.
  ///
  /// Prometheus' docs claim strings aren't currently implemented, but they're
  /// valid as function arguments.
  String(String),

  /// A metric selector
  Selector(Selector),

  /// A grouped expression wrapped in parentheses
  Group(Group),

  /// A function call
  Function(Function),

  /// A binary operator expression
  Operator(Operator)
}

// For handling groups when testing
impl Expression {
  /// Wraps this Expression in a Group, consuming this Expression but returning
  /// an owned Group.
  pub fn group(self) -> Expression {
    Group::new(self).wrap()
  }

  /// Determines a predicted `ReturnValue` for this Expression. A `ReturnValue`
  /// includes a predicted data type and a set of label operations that may
  /// affect which labels are returned.
  pub fn return_value(&self) -> ReturnValue {
    match self {
      Expression::Float(_) => ReturnValue {
        kind: ReturnKind::Scalar,
        label_ops: vec![LabelSetOp::clear(self.clone(), None)]
      },
      Expression::String(_) => ReturnValue {
        kind: ReturnKind::String,
        label_ops: vec![LabelSetOp::clear(self.clone(), None)]
      },
      Expression::Selector(s) => s.return_value(),
      Expression::Group(g) => g.return_value(),
      Expression::Function(f) => f.return_value(),
      Expression::Operator(o) => o.return_value()
    }
  }

  /// If this Expression is a Float, returns its value. Otherwise, returns None.
  pub fn as_f64(&self) -> Option<f64> {
    if let Expression::Float(f) = self {
      Some(*f)
    } else {
      None
    }
  }

  /// If this Expression is a String, returns its value. Otherwise, returns
  /// None.
  pub fn as_str(&self) -> Option<&str> {
    if let Expression::String(s) = self {
      Some(s.as_str())
    } else {
      None
    }
  }
}

impl fmt::Debug for Expression {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    // don't bother formatting the Expression enum itself, this just creates
    // tons of whitespace if we pretty-print
    // this makes the output significantly more readable, if a bit misleading

    // there's gotta be a better way to pass through flags...
    if f.alternate() {
      match self {
        Expression::Float(val) => write!(f, "{:#?}", val),
        Expression::String(val) => write!(f, "{:#?}", val),
        Expression::Selector(val) => write!(f, "{:#?}", val),
        Expression::Group(val) => write!(f, "{:#?}", val),
        Expression::Function(val) => write!(f, "{:#?}", val),
        Expression::Operator(val) => write!(f, "{:#?}", val),
      }
    } else {
      match self {
        Expression::Float(val) => write!(f, "{:?}", val),
        Expression::String(val) => write!(f, "{:?}", val),
        Expression::Selector(val) => write!(f, "{:?}", val),
        Expression::Group(val) => write!(f, "{:?}", val),
        Expression::Function(val) => write!(f, "{:?}", val),
        Expression::Operator(val) => write!(f, "{:?}", val),
      }
    }
  }
}

impl fmt::Display for Expression {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match self {
      Expression::Float(val) => write!(f, "{}", val),
      Expression::String(val) => write!(f, "{}", val),
      Expression::Selector(val) => write!(f, "{}", val),
      Expression::Group(val) => write!(f, "{}", val),
      Expression::Function(val) => write!(f, "{}", val),
      Expression::Operator(val) => write!(f, "{}", val),
    }
  }
}

pub type BExpression = Box<Expression>;