Functional Weave
Code in Python

monitor.counter-increase@1.0.0

impl/rust.rs

2,324 bytes · the Rust implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::monitor_series_window::{samples_from_value, MetricSample};  ← from monitor.series-window ^1.0.0 · built alongside by fune

/// How much a counter grew over the samples, Prometheus-style: a drop means
/// the counter restarted from zero, so the value after it is all increase. No
/// extrapolation to window edges, so the answer is a whole number.
///
/// # Panics
/// Panics on misordered samples or a negative counter value.
pub fn counter_increase(samples: &[MetricSample]) -> CounterIncrease {
    let mut increase: i64 = 0;
    let mut resets: i64 = 0;
    for (i, s) in samples.iter().enumerate() {
        if i > 0 && s.at <= samples[i - 1].at {
            panic!("samples must be in strictly ascending time order: {} follows {}", s.at, samples[i - 1].at);
        }
        if s.value < 0 {
            panic!("counter value must not be negative: {} at {}", s.value, s.at);
        }
        if i == 0 {
            continue;
        }
        let prev = samples[i - 1].value;
        if s.value >= prev {
            increase += s.value - prev;
        } else {
            resets += 1;
            increase += s.value;
        }
    }
    if samples.is_empty() {
        return CounterIncrease { increase: 0, resets: 0, rate_per_second_milli: None, first_at: None, last_at: None };
    }
    let first_at = samples[0].at;
    let last_at = samples[samples.len() - 1].at;
    let rate = if samples.len() >= 2 {
        // increase * 1000 can pass i64 for a large byte counter: i128.
        let n = increase as i128 * 1000;
        let d = (last_at - first_at) as i128;
        Some(((2 * n + d) / (2 * d)) as i64)
    } else {
        None
    };
    CounterIncrease { increase, resets, rate_per_second_milli: rate, first_at: Some(first_at), last_at: Some(last_at) }
}

fn opt(v: Option<i64>) -> Value {
    match v {
        Some(i) => Value::Int(i),
        None => Value::Null,
    }
}

pub fn counter_increase_to_value(c: &CounterIncrease) -> Value {
    Value::obj(vec![
        ("increase", Value::Int(c.increase)),
        ("resets", Value::Int(c.resets)),
        ("ratePerSecondMilli", opt(c.rate_per_second_milli)),
        ("firstAt", opt(c.first_at)),
        ("lastAt", opt(c.last_at)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    counter_increase_to_value(&counter_increase(&samples_from_value(&args[0])))
}