1use core::fmt;
4
5#[derive(Debug)]
12#[non_exhaustive]
13pub enum ArcsecError {
14 Singular,
16 InsufficientStars {
18 found: usize,
20 required: usize,
22 },
23 InsufficientQuads {
28 found: usize,
30 required: usize,
32 },
33 BadSolution {
35 ratio: f64,
37 },
38 CatalogNotFound(std::path::PathBuf),
40 CatalogIo(std::io::Error),
42 InvalidParameter(String),
44}
45
46impl fmt::Display for ArcsecError {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 match self {
49 Self::Singular => write!(f, "singular matrix in LSQ solver"),
50 Self::InsufficientStars { found, required } => {
51 write!(f, "insufficient stars: found {found}, required {required}")
52 }
53 Self::InsufficientQuads { found, required } => {
54 write!(f, "insufficient quads: found {found}, required {required}")
55 }
56 Self::BadSolution { ratio } => {
57 write!(
58 f,
59 "bad solution: xy scale ratio {ratio:.4} not in [0.9, 1.1]"
60 )
61 }
62 Self::CatalogNotFound(p) => write!(f, "catalog not found: {}", p.display()),
63 Self::CatalogIo(e) => write!(f, "catalog I/O error: {e}"),
64 Self::InvalidParameter(msg) => write!(f, "invalid parameter: {msg}"),
65 }
66 }
67}
68
69impl core::error::Error for ArcsecError {
70 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
71 match self {
72 Self::CatalogIo(e) => Some(e),
73 _ => None,
74 }
75 }
76}
77
78impl From<std::io::Error> for ArcsecError {
79 fn from(e: std::io::Error) -> Self {
80 Self::CatalogIo(e)
81 }
82}
83
84pub type Result<T> = core::result::Result<T, ArcsecError>;
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use core::error::Error as _;
91
92 #[test]
94 fn display_messages() {
95 let cases = [
96 (ArcsecError::Singular, "singular matrix in LSQ solver"),
97 (
98 ArcsecError::InsufficientStars {
99 found: 3,
100 required: 5,
101 },
102 "insufficient stars: found 3, required 5",
103 ),
104 (
105 ArcsecError::InsufficientQuads {
106 found: 0,
107 required: 4,
108 },
109 "insufficient quads: found 0, required 4",
110 ),
111 (
112 ArcsecError::BadSolution { ratio: 1.23456 },
113 "bad solution: xy scale ratio 1.2346 not in [0.9, 1.1]",
114 ),
115 (
116 ArcsecError::CatalogNotFound(std::path::PathBuf::from("/db")),
117 "catalog not found: /db",
118 ),
119 (
120 ArcsecError::InvalidParameter("fov".into()),
121 "invalid parameter: fov",
122 ),
123 ];
124 for (err, want) in cases {
125 assert_eq!(err.to_string(), want);
126 assert!(err.source().is_none());
127 }
128 }
129
130 #[test]
131 fn io_errors_convert_and_keep_their_source() {
132 let err: ArcsecError = std::io::Error::new(std::io::ErrorKind::NotFound, "gone").into();
133 assert_eq!(err.to_string(), "catalog I/O error: gone");
134 let source = err.source().expect("source");
135 assert_eq!(source.to_string(), "gone");
136 assert!(
137 matches!(err, ArcsecError::CatalogIo(ref e) if e.kind() == std::io::ErrorKind::NotFound)
138 );
139 }
140}