use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum CspError {
Unsatisfiable,
BudgetExceeded,
InvalidInput {
detail: String,
},
Timeout,
}
impl fmt::Display for CspError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Unsatisfiable => write!(f, "no solution exists under the given constraints"),
Self::BudgetExceeded => {
write!(
f,
"search exceeded its node budget before finding a solution"
)
}
Self::InvalidInput { detail } => write!(f, "invalid input: {detail}"),
Self::Timeout => write!(f, "search exceeded its wall-clock deadline"),
}
}
}
impl std::error::Error for CspError {}
impl CspError {
pub const fn code(&self) -> &'static str {
match self {
Self::Unsatisfiable => "UNSATISFIABLE",
Self::BudgetExceeded => "BUDGET_EXCEEDED",
Self::InvalidInput { .. } => "INVALID_INPUT",
Self::Timeout => "TIMEOUT",
}
}
pub fn invalid_input(detail: impl Into<String>) -> Self {
Self::InvalidInput {
detail: detail.into(),
}
}
}
impl From<crate::Unsatisfiable> for CspError {
fn from(_: crate::Unsatisfiable) -> Self {
CspError::Unsatisfiable
}
}
impl From<crate::AssignmentError> for CspError {
fn from(e: crate::AssignmentError) -> Self {
match e {
crate::AssignmentError::Infeasible => CspError::Unsatisfiable,
other => CspError::invalid_input(other.to_string()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_variant_has_a_stable_code() {
assert_eq!(CspError::Unsatisfiable.code(), "UNSATISFIABLE");
assert_eq!(CspError::BudgetExceeded.code(), "BUDGET_EXCEEDED");
assert_eq!(CspError::invalid_input("x").code(), "INVALID_INPUT");
assert_eq!(CspError::Timeout.code(), "TIMEOUT");
}
#[test]
fn unsatisfiable_marker_converts() {
let e: CspError = crate::Unsatisfiable.into();
assert_eq!(e, CspError::Unsatisfiable);
}
}