Functional Weave
Code in Rust

net.device-status@1.0.0

impl/rust.rs

3,980 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::net_latency_summary::{latency_summary_from_value, LatencySummary};  ← from net.latency-summary ^1.0.0 · built alongside by fune

// Integer arithmetic only, so the reason text is identical in every language.
fn percent(basis_points: i64) -> String {
    format!("{}.{:02}%", basis_points / 100, basis_points % 100)
}

fn ms(us: i64) -> String {
    format!("{}.{:03} ms", us / 1000, us % 1000)
}

/// Up, degraded or down, and every reason, from one latency summary.
///
/// Down wins outright: once a device is down, its p95 and jitter are beside
/// the point. Otherwise every threshold crossed is listed, loss first, so an
/// alert says all that is wrong at once.
///
/// # Panics
/// Panics on an inconsistent summary or thresholds out of range.
pub fn device_status(summary: &LatencySummary, thresholds: &StatusThresholds) -> StatusResult {
    let (sent, received, lost) = (summary.sent, summary.received, summary.lost);
    if sent < 1 || received < 0 || lost < 0 || received + lost != sent {
        panic!(
            "summary is inconsistent: sent {}, received {}, lost {}",
            sent, received, lost
        );
    }
    let deg_loss = thresholds.degraded_loss_basis_points;
    let down_loss = thresholds.down_loss_basis_points;
    let p95_limit = thresholds.degraded_p95_us;
    let jitter_limit = thresholds.degraded_jitter_us;
    if !(1..=10000).contains(&down_loss) {
        panic!("downLossBasisPoints must be 1 to 10000, received {}", down_loss);
    }
    if deg_loss < 1 || deg_loss > down_loss {
        panic!(
            "degradedLossBasisPoints must be 1 to downLossBasisPoints ({}), received {}",
            down_loss, deg_loss
        );
    }
    if p95_limit < 1 {
        panic!("degradedP95Us must be at least 1, received {}", p95_limit);
    }
    if let Some(j) = jitter_limit {
        if j < 1 {
            panic!("degradedJitterUs must be null or at least 1, received {}", j);
        }
    }

    if received == 0 {
        return StatusResult {
            status: "down".to_string(),
            reasons: vec![format!("no replies: {} of {} probes lost", lost, sent)],
        };
    }
    let loss = summary.loss_basis_points;
    if loss >= down_loss {
        return StatusResult {
            status: "down".to_string(),
            reasons: vec![format!(
                "loss {} at or above the down threshold {}",
                percent(loss),
                percent(down_loss)
            )],
        };
    }
    let mut reasons: Vec<String> = Vec::new();
    if loss >= deg_loss {
        reasons.push(format!("loss {} at or above {}", percent(loss), percent(deg_loss)));
    }
    if let Some(p95) = summary.p95_us {
        if p95 >= p95_limit {
            reasons.push(format!("p95 {} at or above {}", ms(p95), ms(p95_limit)));
        }
    }
    if let (Some(limit), Some(jitter)) = (jitter_limit, summary.jitter_us) {
        if jitter >= limit {
            reasons.push(format!("jitter {} at or above {}", ms(jitter), ms(limit)));
        }
    }
    let status = if reasons.is_empty() { "up" } else { "degraded" };
    StatusResult {
        status: status.to_string(),
        reasons,
    }
}

pub fn status_result_to_value(r: &StatusResult) -> Value {
    Value::obj(vec![
        ("status", Value::str(&r.status)),
        (
            "reasons",
            Value::Arr(r.reasons.iter().map(|s| Value::str(s)).collect()),
        ),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    let summary = latency_summary_from_value(&args[0]);
    let t = &args[1];
    let jitter = t.get("degradedJitterUs");
    let thresholds = StatusThresholds {
        degraded_loss_basis_points: t.get("degradedLossBasisPoints").as_i64(),
        down_loss_basis_points: t.get("downLossBasisPoints").as_i64(),
        degraded_p95_us: t.get("degradedP95Us").as_i64(),
        degraded_jitter_us: if jitter.is_null() { None } else { Some(jitter.as_i64()) },
    };
    status_result_to_value(&device_status(&summary, &thresholds))
}