use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
pub(crate) fn check_range(name: &str, value: f64, min: f64, max: f64) -> Result<()> {
if value >= min && value <= max {
Ok(())
} else {
Err(Error::OutOfRange {
parameter: name.to_string(),
min: Some(min),
max: Some(max),
got: value,
range: format!("in [{min}, {max}]"),
})
}
}
pub(crate) fn check_at_least(name: &str, value: f64, min: f64) -> Result<()> {
if value.is_finite() && value >= min {
Ok(())
} else {
Err(Error::OutOfRange {
parameter: name.to_string(),
min: Some(min),
max: None,
got: value,
range: format!("a finite number >= {min}"),
})
}
}
#[derive(Debug, Clone, PartialEq, Error)]
#[non_exhaustive]
pub enum Error {
#[error("{parameter} must be {range}, got {got}")]
OutOfRange {
parameter: String,
min: Option<f64>,
max: Option<f64>,
got: f64,
range: String,
},
#[error("{parameter}: {message}")]
InvalidOption { parameter: String, message: String },
#[error("unknown {parameter} '{got}'; expected one of {}", expected.join(", "))]
UnknownOption {
parameter: String,
got: String,
expected: Vec<String>,
},
#[error("{message}")]
MalformedInput {
parameter: Option<String>,
message: String,
},
#[error("cancelled by the caller")]
Cancelled,
#[error("Invalid geometry: {message}")]
InvalidGeometry { id: Option<String>, message: String },
#[error(
"the id '{id}' is given twice, at positions {first} and {second} of geometries \
(counting from 0); placements name geometries by id, so every geometry needs \
its own"
)]
DuplicateId {
id: String,
first: usize,
second: usize,
},
#[error("Invalid boundary: {message}")]
InvalidBoundary {
parameter: Option<String>,
message: String,
},
#[error("Internal error: {0}")]
Internal(String),
}
impl Error {
pub fn code(&self) -> &'static str {
match self {
Error::OutOfRange { .. } => "parameter_out_of_range",
Error::InvalidOption { .. } => "invalid_option",
Error::UnknownOption { .. } => "unknown_option",
Error::MalformedInput { .. } => "malformed_input",
Error::Cancelled => "cancelled",
Error::InvalidGeometry { .. } => "invalid_geometry",
Error::DuplicateId { .. } => "duplicate_id",
Error::InvalidBoundary { .. } => "invalid_boundary",
Error::Internal(_) => "internal",
}
}
pub fn invalid_option(parameter: &str, message: String) -> Self {
Error::InvalidOption {
parameter: parameter.to_string(),
message,
}
}
pub fn invalid_geometry(id: Option<&str>, message: String) -> Self {
Error::InvalidGeometry {
id: id.map(str::to_string),
message,
}
}
pub fn malformed(parameter: Option<&str>, message: String) -> Self {
Error::MalformedInput {
parameter: parameter.map(str::to_string),
message,
}
}
pub fn invalid_boundary(parameter: Option<&str>, message: String) -> Self {
Error::InvalidBoundary {
parameter: parameter.map(str::to_string),
message,
}
}
}