use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, thiserror::Error)]
#[non_exhaustive]
pub enum SanghaError {
#[error("invalid network: {0}")]
InvalidNetwork(String),
#[error("invalid population: {0}")]
InvalidPopulation(String),
#[error("simulation failed: {0}")]
SimulationFailed(String),
#[error("computation error: {0}")]
ComputationError(String),
}
pub type Result<T> = core::result::Result<T, SanghaError>;
#[inline]
pub(crate) fn validate_finite(value: f64, name: &str) -> Result<()> {
if value.is_finite() {
Ok(())
} else {
Err(SanghaError::ComputationError(format!(
"{name} must be finite, got {value}"
)))
}
}
#[inline]
pub(crate) fn validate_positive(value: f64, name: &str) -> Result<()> {
validate_finite(value, name)?;
if value > 0.0 {
Ok(())
} else {
Err(SanghaError::ComputationError(format!(
"{name} must be positive, got {value}"
)))
}
}
#[inline]
pub(crate) fn validate_non_negative(value: f64, name: &str) -> Result<()> {
validate_finite(value, name)?;
if value >= 0.0 {
Ok(())
} else {
Err(SanghaError::ComputationError(format!(
"{name} must be non-negative, got {value}"
)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = SanghaError::InvalidNetwork("test".into());
assert_eq!(err.to_string(), "invalid network: test");
}
#[test]
fn test_error_serde_roundtrip() {
let err = SanghaError::SimulationFailed("diverged".into());
let json = serde_json::to_string(&err).unwrap();
let back: SanghaError = serde_json::from_str(&json).unwrap();
assert_eq!(err.to_string(), back.to_string());
}
#[test]
fn test_validate_finite() {
assert!(validate_finite(1.0, "x").is_ok());
assert!(validate_finite(f64::NAN, "x").is_err());
}
#[test]
fn test_validate_positive() {
assert!(validate_positive(1.0, "x").is_ok());
assert!(validate_positive(0.0, "x").is_err());
assert!(validate_positive(-1.0, "x").is_err());
}
#[test]
fn test_validate_non_negative() {
assert!(validate_non_negative(0.0, "x").is_ok());
assert!(validate_non_negative(1.0, "x").is_ok());
assert!(validate_non_negative(-0.1, "x").is_err());
assert!(validate_non_negative(f64::INFINITY, "x").is_err());
}
}