use core::fmt;
#[derive(Debug)]
#[non_exhaustive]
pub enum ArcsecError {
Singular,
InsufficientStars {
found: usize,
required: usize,
},
InsufficientQuads {
found: usize,
required: usize,
},
BadSolution {
ratio: f64,
},
CatalogNotFound(std::path::PathBuf),
CatalogIo(std::io::Error),
InvalidParameter(String),
}
impl fmt::Display for ArcsecError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Singular => write!(f, "singular matrix in LSQ solver"),
Self::InsufficientStars { found, required } => {
write!(f, "insufficient stars: found {found}, required {required}")
}
Self::InsufficientQuads { found, required } => {
write!(f, "insufficient quads: found {found}, required {required}")
}
Self::BadSolution { ratio } => {
write!(
f,
"bad solution: plate singular-value ratio {ratio:.4} exceeds {}",
crate::math::lsq::MAX_PLATE_ANISOTROPY
)
}
Self::CatalogNotFound(p) => write!(f, "catalog not found: {}", p.display()),
Self::CatalogIo(e) => write!(f, "catalog I/O error: {e}"),
Self::InvalidParameter(msg) => write!(f, "invalid parameter: {msg}"),
}
}
}
impl core::error::Error for ArcsecError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::CatalogIo(e) => Some(e),
_ => None,
}
}
}
impl From<std::io::Error> for ArcsecError {
fn from(e: std::io::Error) -> Self {
Self::CatalogIo(e)
}
}
pub type Result<T> = core::result::Result<T, ArcsecError>;
#[cfg(test)]
mod tests {
use super::*;
use core::error::Error as _;
#[test]
fn display_messages() {
let cases = [
(ArcsecError::Singular, "singular matrix in LSQ solver"),
(
ArcsecError::InsufficientStars {
found: 3,
required: 5,
},
"insufficient stars: found 3, required 5",
),
(
ArcsecError::InsufficientQuads {
found: 0,
required: 4,
},
"insufficient quads: found 0, required 4",
),
(
ArcsecError::BadSolution { ratio: 1.23456 },
"bad solution: plate singular-value ratio 1.2346 exceeds 1.08",
),
(
ArcsecError::CatalogNotFound(std::path::PathBuf::from("/db")),
"catalog not found: /db",
),
(
ArcsecError::InvalidParameter("fov".into()),
"invalid parameter: fov",
),
];
for (err, want) in cases {
assert_eq!(err.to_string(), want);
assert!(err.source().is_none());
}
}
#[test]
fn io_errors_convert_and_keep_their_source() {
let err: ArcsecError = std::io::Error::new(std::io::ErrorKind::NotFound, "gone").into();
assert_eq!(err.to_string(), "catalog I/O error: gone");
let source = err.source().expect("source");
assert_eq!(source.to_string(), "gone");
assert!(
matches!(err, ArcsecError::CatalogIo(ref e) if e.kind() == std::io::ErrorKind::NotFound)
);
}
}