#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum MaxFailureThreshold {
Absolute(usize),
Percent(f64),
}
impl MaxFailureThreshold {
pub fn limit_for(&self, world_size: usize) -> usize {
match self {
MaxFailureThreshold::Absolute(n) => *n,
MaxFailureThreshold::Percent(p) => {
let clamped = p.clamp(0.0, 1.0);
(clamped * world_size as f64).ceil() as usize
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn absolute_limit_returns_n_directly() {
let t = MaxFailureThreshold::Absolute(3);
assert_eq!(t.limit_for(10), 3);
assert_eq!(t.limit_for(4), 3);
assert_eq!(t.limit_for(2), 3); }
#[test]
fn percent_limit_rounds_up() {
let t = MaxFailureThreshold::Percent(0.2);
assert_eq!(t.limit_for(10), 2); assert_eq!(t.limit_for(11), 3); assert_eq!(t.limit_for(5), 1); assert_eq!(t.limit_for(3), 1); }
#[test]
fn percent_zero_is_any_death_triggers() {
let t = MaxFailureThreshold::Percent(0.0);
assert_eq!(t.limit_for(10), 0);
}
#[test]
fn percent_one_is_all_dead_triggers() {
let t = MaxFailureThreshold::Percent(1.0);
assert_eq!(t.limit_for(10), 10);
assert_eq!(t.limit_for(1), 1);
}
#[test]
fn percent_out_of_range_is_clamped() {
let t_high = MaxFailureThreshold::Percent(2.5);
assert_eq!(t_high.limit_for(10), 10);
let t_low = MaxFailureThreshold::Percent(-0.3);
assert_eq!(t_low.limit_for(10), 0);
}
#[test]
fn percent_handles_zero_world_size() {
let t = MaxFailureThreshold::Percent(0.5);
assert_eq!(t.limit_for(0), 0);
}
}