Skip to main content

fitscube_rs/
error.rs

1//! Error types for fitscube-rs.
2//!
3//! Mirrors the exception hierarchy of the original `fitscube` Python package
4//! ([`fitscube.exceptions`](https://github.com/AlecThomson/fitscube)): a base
5//! [`FitsCubeError`] with dedicated variants for a missing target (FREQ/TIME)
6//! axis and an inaccessible channel, plus the I/O and parsing errors that arise
7//! in the Rust implementation.
8use std::path::PathBuf;
9
10use thiserror::Error;
11
12/// Top-level error for all fitscube-rs operations.
13///
14/// `TargetAxisMissing` and `ChannelMissing` correspond to the Python
15/// `TargetAxisMissingException` and `ChannelMissingException`.
16#[derive(Debug, Error)]
17pub enum FitsCubeError {
18    /// Underlying cfitsio error.
19    #[error("FITS I/O error: {0}")]
20    Fits(#[from] fitsio::errors::Error),
21
22    /// Filesystem I/O error.
23    #[error("I/O error: {0}")]
24    Io(#[from] std::io::Error),
25
26    /// ndarray shape mismatch when reshaping a flat plane buffer.
27    #[error("shape error: {0}")]
28    Shape(#[from] ndarray::ShapeError),
29
30    /// A required header keyword was absent.
31    #[error("missing header keyword: {0}")]
32    MissingKeyword(String),
33
34    /// The FREQ/TIME (or other requested) axis was not found in the WCS.
35    ///
36    /// Equivalent to Python `TargetAxisMissingException`.
37    #[error("target axis missing: {0}")]
38    TargetAxisMissing(String),
39
40    /// A requested channel/timestep could not be accessed.
41    ///
42    /// Equivalent to Python `ChannelMissingException`.
43    #[error("channel missing: {0}")]
44    ChannelMissing(String),
45
46    /// The output file already exists and `overwrite` was not set.
47    #[error("output file {0} already exists (use overwrite)")]
48    OutputExists(PathBuf),
49
50    /// Mutually exclusive spectral-input options were combined, or a count
51    /// mismatch was detected.
52    #[error("invalid spectral input: {0}")]
53    InvalidSpec(String),
54
55    /// A FITS image had an unexpected dimensionality.
56    #[error("unsupported NAXIS={0}")]
57    UnsupportedNaxis(i64),
58
59    /// Failed to parse a DATE-OBS timestamp.
60    #[error("could not parse DATE-OBS {value:?}: {msg}")]
61    TimeParse { value: String, msg: String },
62
63    /// Catch-all for invariant violations.
64    #[error("{0}")]
65    Other(String),
66}
67
68/// Map the shared [`atfits_rs::AtfitsError`] into the fitscube-rs hierarchy.
69///
70/// The low-level cfitsio helpers (keyword editing, image creation, axis lookup)
71/// live in `atfits-rs`; this lets a `?` on any of them flow into a
72/// [`FitsCubeError`] with the matching variant.
73impl From<atfits_rs::AtfitsError> for FitsCubeError {
74    fn from(e: atfits_rs::AtfitsError) -> Self {
75        use atfits_rs::AtfitsError as A;
76        match e {
77            A::Fits(e) => FitsCubeError::Fits(e),
78            A::Io(e) => FitsCubeError::Io(e),
79            A::MissingKeyword(s) => FitsCubeError::MissingKeyword(s),
80            A::TargetAxisMissing(s) => FitsCubeError::TargetAxisMissing(s),
81            A::UnsupportedNaxis(n) => FitsCubeError::UnsupportedNaxis(n),
82            A::Other(s) => FitsCubeError::Other(s),
83        }
84    }
85}
86
87/// Convenience result alias.
88pub type Result<T> = std::result::Result<T, FitsCubeError>;