Functional Weave
Code in Python

monitor.cert-expiry@1.0.0

impl/rust.rs

2,344 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

/// RFC 5280 makes validity inclusive of notAfter, so a certificate is expired
/// only after that second, not at it. Thresholds compare whole days left with
/// the day counts: fewer than N days (secondsLeft < N * 86400) is exactly
/// floor(secondsLeft / 86400) < N, with no multiplication to overflow.
///
/// # Panics
/// Panics when critical_days is negative or warn_days is below critical_days.
pub fn cert_expiry(not_after: i64, now: i64, warn_days: i64, critical_days: i64) -> CertExpiry {
    if critical_days < 0 {
        panic!("criticalDays must be a whole number 0 or more, received {}", critical_days);
    }
    if warn_days < critical_days {
        panic!("warnDays must be a whole number at least criticalDays ({}), received {}", critical_days, warn_days);
    }
    if now > not_after {
        return CertExpiry { state: "expired".to_string(), seconds_left: 0, days_left: 0 };
    }
    let seconds_left = not_after - now;
    let days_left = seconds_left / 86400;
    let state = if days_left < critical_days {
        "critical"
    } else if days_left < warn_days {
        "warning"
    } else {
        "ok"
    };
    CertExpiry { state: state.to_string(), seconds_left, days_left }
}

pub fn cert_expiry_to_value(c: &CertExpiry) -> Value {
    Value::obj(vec![
        ("state", Value::str(&c.state)),
        ("secondsLeft", Value::Int(c.seconds_left)),
        ("daysLeft", Value::Int(c.days_left)),
    ])
}

fn show(v: &Value) -> String {
    match v {
        Value::Int(i) => i.to_string(),
        Value::Float(f) => f.to_string(),
        _ => "a non-number".to_string(),
    }
}

pub fn fune_vector(args: &[Value]) -> Value {
    if !matches!(args[0], Value::Int(_)) || !matches!(args[1], Value::Int(_)) {
        panic!("notAfter and now must be whole seconds, received {} and {}", show(&args[0]), show(&args[1]));
    }
    let critical = match &args[3] {
        Value::Int(i) => *i,
        other => panic!("criticalDays must be a whole number 0 or more, received {}", show(other)),
    };
    let warn = match &args[2] {
        Value::Int(i) => *i,
        other => panic!("warnDays must be a whole number at least criticalDays ({}), received {}", critical, show(other)),
    };
    cert_expiry_to_value(&cert_expiry(args[0].as_i64(), args[1].as_i64(), warn, critical))
}