///|
fn label_value(labels : @model.LabelSet, name : String) -> String {
labels.get(name).unwrap_or("")
}
///|
fn regex_matches(
pattern : String,
value : String,
) -> Bool raise @model.PromqlError {
let anchored = "^(?:\{pattern})$"
let regex = @string.Regex(anchored) catch {
_ => raise @model.EvalError("invalid regular expression `\{pattern}`")
}
regex.execute(value) is Some(_)
}
///|
fn matcher_matches(
labels : @model.LabelSet,
matcher : @ast.LabelMatcher,
) -> Bool raise @model.PromqlError {
let actual = label_value(labels, matcher.name)
match matcher.op {
@ast.MatchOp::Equal => actual == matcher.value
@ast.MatchOp::NotEqual => actual != matcher.value
@ast.MatchOp::Regex => regex_matches(matcher.value, actual)
@ast.MatchOp::NotRegex => !regex_matches(matcher.value, actual)
}
}
///|
fn selector_matches(
series : @model.Series,
selector : @ast.Selector,
) -> Bool raise @model.PromqlError {
if selector.metric != "" && series.metric != selector.metric {
return false
}
for matcher in selector.matchers {
if !matcher_matches(series.labels, matcher) {
return false
}
}
true
}
///|
fn selector_eval_time(
selector : @ast.Selector,
context : EvalContext,
) -> Double {
let base = selector.at_ms.unwrap_or(context.eval_time_ms)
base - selector.offset_ms
}
///|
fn latest_sample(
samples : Array[@model.Sample],
end_ms : Double,
) -> @model.Sample? {
let mut result : @model.Sample? = None
for sample in samples {
if sample.timestamp <= end_ms {
if result is None || sample.timestamp >= result.unwrap().timestamp {
result = Some(sample)
}
} else {
break
}
}
match result {
None => None
Some(sample) if end_ms - sample.timestamp > 300000.0 => None
Some(_) => result
}
}
///|
fn select_instant(
selector : @ast.Selector,
context : EvalContext,
) -> @model.Value raise @model.PromqlError {
let end_ms = selector_eval_time(selector, context)
let points : Array[@model.Point] = []
for series in context.series {
if selector_matches(series, selector) {
match latest_sample(series.samples, end_ms) {
Some(sample) =>
points.push({ labels: series.labels, value: sample.value, })
None => ()
}
}
}
points.sort_by((left, right) => {
left.labels.to_string().compare(right.labels.to_string())
})
@model.Instant(points)
}
///|
fn select_range(
selector : @ast.Selector,
range_ms : Double,
context : EvalContext,
) -> @model.Value raise @model.PromqlError {
let end_ms = selector_eval_time(selector, context)
let start_ms = end_ms - range_ms
let output : Array[@model.RangeSeries] = []
for series in context.series {
if selector_matches(series, selector) {
let samples : Array[@model.Sample] = []
for sample in series.samples {
if sample.timestamp > start_ms && sample.timestamp <= end_ms {
samples.push(sample)
}
}
if !samples.is_empty() {
output.push({ labels: series.labels, samples, })
}
}
}
output.sort_by((left, right) => {
left.labels.to_string().compare(right.labels.to_string())
})
@model.Range(output)
}