use crate::SddpError;
#[derive(Debug, Clone)]
pub enum HorizonMode {
Finite {
num_stages: usize,
},
}
impl HorizonMode {
#[must_use]
pub fn is_terminal(&self, stage: usize) -> bool {
match self {
HorizonMode::Finite { num_stages } => stage >= *num_stages,
}
}
pub fn validate(&self) -> Result<(), SddpError> {
match self {
HorizonMode::Finite { num_stages } => {
if *num_stages < 2 {
return Err(SddpError::Validation(format!(
"HorizonMode::Finite requires at least 2 stages, got {num_stages}"
)));
}
Ok(())
}
}
}
#[must_use]
pub fn num_stages(&self) -> usize {
match self {
HorizonMode::Finite { num_stages } => *num_stages,
}
}
}
#[cfg(test)]
mod tests {
use super::HorizonMode;
use crate::SddpError;
#[test]
fn is_terminal_last_stage_is_true() {
let h = HorizonMode::Finite { num_stages: 5 };
assert!(h.is_terminal(5));
}
#[test]
fn is_terminal_preceding_stage_is_false() {
let h = HorizonMode::Finite { num_stages: 5 };
assert!(!h.is_terminal(4));
}
#[test]
fn is_terminal_first_stage_is_false() {
let h = HorizonMode::Finite { num_stages: 5 };
assert!(!h.is_terminal(1));
}
#[test]
fn is_terminal_single_stage_is_terminal() {
let h = HorizonMode::Finite { num_stages: 1 };
assert!(h.is_terminal(1));
}
#[test]
fn validate_accepts_two_or_more_stages() {
for n in [2, 3, 10, 60, 120] {
let h = HorizonMode::Finite { num_stages: n };
assert!(h.validate().is_ok(), "num_stages={n} should be valid");
}
}
#[test]
fn validate_rejects_one_stage() {
let h = HorizonMode::Finite { num_stages: 1 };
let result = h.validate();
assert!(
matches!(result, Err(SddpError::Validation(_))),
"expected Err(Validation), got {result:?}"
);
}
#[test]
fn validate_rejects_zero_stages() {
let h = HorizonMode::Finite { num_stages: 0 };
let result = h.validate();
assert!(
matches!(result, Err(SddpError::Validation(_))),
"expected Err(Validation), got {result:?}"
);
}
#[test]
fn validate_error_message_contains_stage_count() {
let h = HorizonMode::Finite { num_stages: 1 };
let err = h.validate().unwrap_err();
let msg = err.to_string();
assert!(
msg.contains('1'),
"error message should contain the invalid stage count: {msg}"
);
}
#[test]
fn num_stages_returns_field_value() {
let h = HorizonMode::Finite { num_stages: 12 };
assert_eq!(h.num_stages(), 12);
}
#[test]
fn num_stages_single() {
let h = HorizonMode::Finite { num_stages: 1 };
assert_eq!(h.num_stages(), 1);
}
#[test]
fn debug_output_contains_variant_name() {
let h = HorizonMode::Finite { num_stages: 5 };
let debug_str = format!("{h:?}");
assert!(debug_str.contains("Finite"));
assert!(debug_str.contains("num_stages"));
}
#[test]
fn clone_produces_equal_num_stages() {
let h = HorizonMode::Finite { num_stages: 8 };
let cloned = h.clone();
assert_eq!(cloned.num_stages(), h.num_stages());
}
}