use crate::dsl::factory::ConstArg;
#[derive(Debug, Clone, Copy)]
pub enum ConstConstraint {
RangeU64 { min: u64, max: u64 },
RangeF64 { min: f64, max: f64 },
AllowedU64(&'static [u64]),
NonZeroU64,
NonEmptyStr,
StrParser(fn(&str) -> Result<(), String>),
PositiveFiniteF64,
FiniteF64,
}
impl ConstConstraint {
pub fn check(&self, arg: &ConstArg, param_name: &str) -> Result<(), String> {
match self {
ConstConstraint::RangeU64 { min, max } => {
let v = arg.as_u64();
if v < *min || v > *max {
Err(format!("{param_name} must be in [{min}, {max}], got {v}"))
} else { Ok(()) }
}
ConstConstraint::RangeF64 { min, max } => {
let v = arg.as_f64();
if !(*min..=*max).contains(&v) {
Err(format!("{param_name} must be in [{min}, {max}], got {v}"))
} else { Ok(()) }
}
ConstConstraint::AllowedU64(allowed) => {
let v = arg.as_u64();
if !allowed.contains(&v) {
Err(format!("{param_name} must be one of {allowed:?}, got {v}"))
} else { Ok(()) }
}
ConstConstraint::NonZeroU64 => {
let v = arg.as_u64();
if v == 0 {
Err(format!("{param_name} must be non-zero"))
} else { Ok(()) }
}
ConstConstraint::NonEmptyStr => {
let s = arg.as_str();
if s.trim().is_empty() {
Err(format!("{param_name} must be non-empty"))
} else { Ok(()) }
}
ConstConstraint::StrParser(f) => {
let s = arg.as_str();
f(s).map_err(|e| format!("{param_name}: {e}"))
}
ConstConstraint::PositiveFiniteF64 => {
let v = arg.as_f64();
if !v.is_finite() || v <= 0.0 {
Err(format!("{param_name} must be a positive finite f64, got {v}"))
} else { Ok(()) }
}
ConstConstraint::FiniteF64 => {
let v = arg.as_f64();
if !v.is_finite() {
Err(format!("{param_name} must be a finite f64, got {v}"))
} else { Ok(()) }
}
}
}
}
pub type NodeValidator = fn(name: &str, consts: &[ConstArg]) -> Result<(), String>;
pub fn check_opt(
constraint: &ConstConstraint,
consts: &[ConstArg],
index: usize,
param_name: &str,
) -> Result<(), String> {
match consts.get(index) {
Some(arg) => constraint.check(arg, param_name),
None => Ok(()),
}
}