use super::funejson::Value; use super::dates_age::age; /// 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 = 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))]) }