use super::funejson::Value; use super::dates_add_days::{add_days, days_between, parse_iso_date}; /// Naegele's rule: 280 days (40+0 weeks) from the LMP for a 28-day cycle. const TERM_DAYS: i64 = 280; /// 44+0 weeks: beyond this the dates are almost certainly wrong. const MAX_DAYS: i64 = 308; /// Gestational age and estimated due date from the last menstrual period. A /// cycle longer or shorter than 28 days moves ovulation, so both are shifted by /// the difference, as the NHS due date calculator does. /// /// # Panics /// Panics on a cycle length outside 20..=44, a malformed date, or a /// gestational age below zero or above 44+0 weeks. pub fn gestational_age(lmp_date: &str, on_date: &str, cycle_length_days: i64) -> GestationalAge { if !(20..=44).contains(&cycle_length_days) { panic!("cycleLengthDays must be a whole number from 20 to 44, received {}", cycle_length_days); } parse_iso_date(lmp_date); parse_iso_date(on_date); let shift = cycle_length_days - 28; let total_days = days_between(lmp_date, on_date) - shift; if total_days < 0 { panic!("onDate {} is before the adjusted start of pregnancy", on_date); } if total_days > MAX_DAYS { panic!("gestational age of {} days is more than 44+0 weeks: check the dates", total_days); } GestationalAge { weeks: total_days / 7, days: total_days % 7, total_days, due_date: add_days(lmp_date, TERM_DAYS + shift), } } pub fn gestational_age_to_value(age: &GestationalAge) -> Value { Value::obj(vec![ ("weeks", Value::Int(age.weeks)), ("days", Value::Int(age.days)), ("totalDays", Value::Int(age.total_days)), ("dueDate", Value::str(&age.due_date)), ]) } fn date_arg(value: &Value) -> &str { match value { Value::Str(s) => s.as_str(), _ => panic!("{:?} is not an ISO date (YYYY-MM-DD)", value), } } pub fn fune_vector(args: &[Value]) -> Value { // Refuse what the typed signature cannot hold, with the wording TypeScript // and Python use, rather than let the conversion below quietly change it. let cycle = match args[2] { Value::Int(i) => i, _ => panic!("cycleLengthDays must be a whole number from 20 to 44, received {:?}", args[2]), }; gestational_age_to_value(&gestational_age(date_arg(&args[0]), date_arg(&args[1]), cycle)) }