use core::fmt;
pub type SpringCutoff = (f64, f64);
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Spring {
pub k: f64,
pub l_0: f64,
pub cutoff: Option<SpringCutoff>,
}
impl Spring {
pub fn new(k: f64, l_0: f64, cutoff: Option<SpringCutoff>) -> Result<Self, SpringLawError> {
let spring = Self { k, l_0, cutoff };
spring.validate()?;
Ok(spring)
}
pub fn validate(&self) -> Result<(), SpringLawError> {
if !self.k.is_finite() {
return Err(SpringLawError::InvalidSpringConstant { k: self.k });
}
if !self.l_0.is_finite() || self.l_0 < 0.0 {
return Err(SpringLawError::InvalidRestLength { l_0: self.l_0 });
}
if let Some((min, max)) = self.cutoff {
if !min.is_finite() || !max.is_finite() || min < 0.0 || max < min {
return Err(SpringLawError::InvalidCutoff { min, max });
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum SpringLawError {
InvalidSpringConstant {
k: f64,
},
InvalidRestLength {
l_0: f64,
},
InvalidCutoff {
min: f64,
max: f64,
},
}
impl fmt::Display for SpringLawError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidSpringConstant { k } => {
write!(f, "spring constant must be finite; got {k}")
}
Self::InvalidRestLength { l_0 } => {
write!(
f,
"spring rest length must be finite and non-negative; got {l_0}"
)
}
Self::InvalidCutoff { min, max } => write!(
f,
"spring cutoff must satisfy finite 0 <= min <= max; got min={min}, max={max}"
),
}
}
}
impl std::error::Error for SpringLawError {}