arcsec_core/error.rs
1//! The crate's error type.
2
3use core::fmt;
4
5/// Everything that can stop a solve.
6///
7/// The variants map onto the ASTAP-compatible exit codes the `arcsec` CLI returns
8/// (2 for too few stars, 1 for no solution, 32/33 for database problems), which is
9/// why "no match" is reported through [`ArcsecError::InsufficientQuads`] rather than
10/// a dedicated variant. New variants may be added, so match with a wildcard arm.
11#[derive(Debug)]
12#[non_exhaustive]
13pub enum ArcsecError {
14 /// The least-squares system was singular (degenerate or too few points).
15 Singular,
16 /// Fewer stars were detected than the solver needs.
17 InsufficientStars {
18 /// Stars actually detected.
19 found: usize,
20 /// Minimum the solver requires.
21 required: usize,
22 },
23 /// Pattern matching did not produce a verified solution.
24 ///
25 /// Returned both when too few patterns could be built and when the search
26 /// finished without any position verifying.
27 InsufficientQuads {
28 /// Patterns (or, for the blind solver, verification score) achieved.
29 found: usize,
30 /// Minimum required.
31 required: usize,
32 },
33 /// The plate fit's X and Y scales disagree by more than 10%.
34 BadSolution {
35 /// Ratio of the squared X scale to the squared Y scale.
36 ratio: f64,
37 },
38 /// The star database directory does not contain the named database.
39 CatalogNotFound(std::path::PathBuf),
40 /// A catalogue or index file could not be read or parsed.
41 CatalogIo(std::io::Error),
42 /// A caller-supplied parameter is out of range (e.g. a non-positive field of view).
43 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
84/// `Result` specialised to [`ArcsecError`].
85pub type Result<T> = core::result::Result<T, ArcsecError>;