Functional Weave
Code in Python

insurance.age-banding@1.0.1

impl/rust.rs

2,813 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::dates_age::age;  ← from dates.age ^1.0.0 · built alongside by fune

/// The policyholder's age on the inception date and the band it falls in.
///
/// Age is completed years by dates.age, so a 29 February birthday ages on
/// 1 March in other years. The bands are checked before use: youngest first,
/// inclusive at both ends, no overlaps, and only the last may be open-ended.
/// An age no band covers is an error, not the nearest band.
///
/// # Panics
/// Panics on malformed bands, dates dates.age refuses, or an age no band covers.
pub fn age_band(birth_date: &str, inception_date: &str, bands: &[AgeBand]) -> AgeBanding {
    if bands.is_empty() {
        panic!("bands must not be empty");
    }
    let mut previous_max: i64 = -1;
    for (i, band) in bands.iter().enumerate() {
        if band.min_age < 0 {
            panic!("minAge must be a whole number of 0 or more, received {}", band.min_age);
        }
        if band.max_age.is_none() && i != bands.len() - 1 {
            panic!("only the last band may have no maxAge: \"{}\" is not last", band.label);
        }
        if let Some(max) = band.max_age {
            if max < band.min_age {
                panic!("maxAge must be a whole number no less than minAge in band \"{}\"", band.label);
            }
        }
        if band.min_age <= previous_max {
            panic!(
                "bands must be youngest first and not overlap: \"{}\" starts at {}",
                band.label, band.min_age
            );
        }
        previous_max = band.max_age.unwrap_or(i64::MAX);
    }
    let years = age(birth_date, inception_date);
    for band in bands {
        if years >= band.min_age && band.max_age.map_or(true, |max| years <= max) {
            return AgeBanding {
                age: years,
                label: band.label.clone(),
            };
        }
    }
    panic!("no band for age {}", years)
}

pub fn fune_vector(args: &[Value]) -> Value {
    let bands: Vec<AgeBand> = args[2]
        .as_arr()
        .iter()
        .map(|b| {
            if let Value::Float(f) = b.get("minAge") {
                panic!("minAge must be a whole number of 0 or more, received {}", f);
            }
            if let Value::Float(_) = b.get("maxAge") {
                panic!("maxAge must be a whole number no less than minAge in band \"{}\"", b.get("label").as_str());
            }
            AgeBand {
                label: b.get("label").as_str().to_string(),
                min_age: b.get("minAge").as_i64(),
                max_age: if b.get("maxAge").is_null() { None } else { Some(b.get("maxAge").as_i64()) },
            }
        })
        .collect();
    let result = age_band(args[0].as_str(), args[1].as_str(), &bands);
    Value::obj(vec![("age", Value::Int(result.age)), ("label", Value::str(&result.label))])
}