use crate::mt_config::MortTableConfig;
use bon::Builder;
use garde::Validate;
use super::validation::{
ErrorVec, collect_age_bounds_errors, validate_age_boundaries, validate_entry_age,
};
#[derive(Debug, Clone, Validate, Builder)]
#[garde(allow_unvalidated)]
pub struct GetValueFunctionValidation {
#[garde(dive)]
pub mt: MortTableConfig,
#[garde(range(min = 0.0, max = 130.0))]
pub x: f64,
#[garde(range(max = 130))]
pub entry_age: Option<u32>,
}
impl GetValueFunctionValidation {
pub fn validate_all(&self) -> Result<(), garde::Report> {
self.validate()?;
self.validate_custom_constraints()
}
fn validate_custom_constraints(&self) -> Result<(), garde::Report> {
let mut report = garde::Report::new();
let mut errors: ErrorVec = Vec::new();
let age_bounds = collect_age_bounds_errors(&self.mt, &mut errors);
let (min_age, max_age) = match age_bounds {
Some(bounds) => bounds,
None => {
for (path, message) in errors {
report.append(garde::Path::new(path), garde::Error::new(message));
}
return Err(report);
}
};
validate_age_boundaries(self.x, min_age, max_age, &mut errors);
validate_entry_age(self.entry_age, self.x, &mut errors);
for (path, message) in errors {
report.append(garde::Path::new(path), garde::Error::new(message));
}
if report.is_empty() {
Ok(())
} else {
Err(report)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mt_config::mt_data::MortData;
#[test]
fn test_negative_age_validation() {
let mort_data = MortData::from_builtin("AM92").unwrap();
let mt = MortTableConfig::builder().data(mort_data).build().unwrap();
let params = GetValueFunctionValidation::builder()
.mt(mt.clone())
.x(-5.0)
.build();
let result = params.validate_all();
assert!(result.is_err(), "Negative age should fail validation");
}
#[test]
fn test_age_below_min_age() {
let mort_data = MortData::from_builtin("AM92").unwrap();
let mt = MortTableConfig::builder().data(mort_data).build().unwrap();
let min_age = mt.min_age().unwrap();
let below_min = (min_age as f64) - 1.0;
let params = GetValueFunctionValidation::builder()
.mt(mt.clone())
.x(below_min)
.build();
let result = params.validate_all();
assert!(
result.is_err(),
"Age {} should fail validation (min_age is {})",
below_min,
min_age
);
let params = GetValueFunctionValidation::builder()
.mt(mt.clone())
.x(min_age as f64)
.build();
let result = params.validate_all();
assert!(
result.is_ok(),
"Age {} (min_age) should pass validation",
min_age
);
}
#[test]
fn test_age_exceeds_table_max_age() {
let mort_data = MortData::from_builtin("AM92").unwrap();
let mt = MortTableConfig::builder().data(mort_data).build().unwrap();
let max_age = mt.max_age().unwrap();
let above_max = (max_age as f64) + 1.0;
let params = GetValueFunctionValidation::builder()
.mt(mt.clone())
.x(above_max)
.build();
let result = params.validate_all();
assert!(
result.is_err(),
"Age {} should fail validation (max_age is {})",
above_max,
max_age
);
let params = GetValueFunctionValidation::builder()
.mt(mt.clone())
.x(max_age as f64)
.build();
let result = params.validate_all();
assert!(
result.is_ok(),
"Age {} (max_age) should pass validation",
max_age
);
}
#[test]
fn test_entry_age_exceeds_age_x() {
let mort_data = MortData::from_builtin("AM92").unwrap();
let mt = MortTableConfig::builder().data(mort_data).build().unwrap();
let params = GetValueFunctionValidation::builder()
.mt(mt.clone())
.x(30.0)
.entry_age(35)
.build();
let result = params.validate_all();
assert!(result.is_err(), "entry_age 35 should fail when age x is 30");
}
#[test]
fn test_entry_age_non_negative() {
let mort_data = MortData::from_builtin("AM92").unwrap();
let mt = MortTableConfig::builder().data(mort_data).build().unwrap();
let params = GetValueFunctionValidation::builder()
.mt(mt.clone())
.x(50.0)
.entry_age(0)
.build();
let result = params.validate_all();
assert!(
result.is_ok(),
"entry_age 0 should pass validation (non-negative)"
);
}
#[test]
fn test_entry_age_valid() {
let mort_data = MortData::from_builtin("AM92").unwrap();
let mt = MortTableConfig::builder().data(mort_data).build().unwrap();
let params = GetValueFunctionValidation::builder()
.mt(mt.clone())
.x(40.0)
.entry_age(40)
.build();
let result = params.validate_all();
assert!(
result.is_ok(),
"entry_age equal to x should pass validation"
);
let params = GetValueFunctionValidation::builder()
.mt(mt.clone())
.x(40.0)
.build();
let result = params.validate_all();
assert!(result.is_ok(), "entry_age = None should pass validation");
}
}