rslife 0.2.13

A comprehensive Rust library for actuarial mortality table calculations and life insurance mathematics
Documentation
use crate::mt_config::MortTableConfig;

pub type ErrorVec = Vec<(&'static str, String)>;

pub fn collect_age_bounds_errors(
    mt: &MortTableConfig,
    errors: &mut ErrorVec,
) -> Option<(f64, f64)> {
    let min_age_result = mt.min_age();
    let max_age_result = mt.max_age();
    match (&min_age_result, &max_age_result) {
        (Ok(min), Ok(max)) => Some((*min as f64, *max as f64)),
        _ => {
            if min_age_result.is_err() {
                errors.push(("mt", "Failed to get min_age from mortality table".into()));
            }
            if max_age_result.is_err() {
                errors.push(("mt", "Failed to get max_age from mortality table".into()));
            }
            None
        }
    }
}

pub fn validate_age_boundaries(x: f64, min_age: f64, max_age: f64, errors: &mut ErrorVec) {
    if x < min_age || x > max_age {
        errors.push((
            "x",
            format!("age {x} must be between {min_age} and {max_age} from mortality table"),
        ));
    }
}

pub fn validate_entry_age(entry_age: Option<u32>, x: f64, errors: &mut ErrorVec) {
    if let Some(entry_age) = entry_age {
        let entry_age_f = entry_age as f64;
        if entry_age_f > x {
            errors.push((
                "entry_age",
                format!("entry_age {entry_age} cannot exceed age {x}"),
            ));
        }
    }
}

// ===========================================================================
// UNIT TEST
// ===========================================================================

#[cfg(test)]
mod tests {
    use super::*;
    use crate::mt_config::mt_data::MortData;

    fn create_test_mt() -> MortTableConfig {
        let mort_data = MortData::from_builtin("AM92").unwrap();
        MortTableConfig::builder().data(mort_data).build().unwrap()
    }

    #[test]
    fn test_collect_age_bounds_errors_success() {
        let mt = create_test_mt();
        let mut errors: ErrorVec = Vec::new();
        let result = collect_age_bounds_errors(&mt, &mut errors);
        assert!(result.is_some());
        assert!(errors.is_empty());
        let (min, max) = result.unwrap();
        assert_eq!(min, 17.0);
        assert!(max > 100.0);
    }

    #[test]
    fn test_validate_age_boundaries_valid() {
        let mut errors: ErrorVec = Vec::new();
        validate_age_boundaries(50.0, 17.0, 120.0, &mut errors);
        assert!(errors.is_empty());
    }

    #[test]
    fn test_validate_age_boundaries_below_min() {
        let mut errors: ErrorVec = Vec::new();
        validate_age_boundaries(10.0, 17.0, 120.0, &mut errors);
        assert_eq!(errors.len(), 1);
        assert_eq!(errors[0].0, "x");
    }

    #[test]
    fn test_validate_age_boundaries_above_max() {
        let mut errors: ErrorVec = Vec::new();
        validate_age_boundaries(130.0, 17.0, 120.0, &mut errors);
        assert_eq!(errors.len(), 1);
    }

    #[test]
    fn test_validate_entry_age_none() {
        let mut errors: ErrorVec = Vec::new();
        validate_entry_age(None, 50.0, &mut errors);
        assert!(errors.is_empty());
    }

    #[test]
    fn test_validate_entry_age_valid() {
        let mut errors: ErrorVec = Vec::new();
        validate_entry_age(Some(30), 50.0, &mut errors);
        assert!(errors.is_empty());
    }

    #[test]
    fn test_validate_entry_age_exceeds_x() {
        let mut errors: ErrorVec = Vec::new();
        validate_entry_age(Some(60), 50.0, &mut errors);
        assert_eq!(errors.len(), 1);
        assert_eq!(errors[0].0, "entry_age");
    }

    #[test]
    fn test_validate_entry_age_zero() {
        let mut errors: ErrorVec = Vec::new();
        validate_entry_age(Some(0), 50.0, &mut errors);
        assert!(errors.is_empty());
    }
}