xisf_rs/
error.rs

1//! All of the error types returned by API functions
2
3use std::{
4    error::Error,
5    fmt::{Display, Formatter},
6};
7
8use url::{Host, Url};
9
10/// A failure to parse a simple value such as a primitive type or an enum, usually from an XML attribute
11#[derive(Clone, Copy, Debug, PartialEq)]
12pub struct ParseValueError(pub &'static str);
13impl Display for ParseValueError {
14    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
15        f.write_fmt(format_args!("Failed to parse {}", self.0))
16    }
17}
18impl Error for ParseValueError {}
19
20/// A failure to parse a [FITS key](crate::image::FitsKeyContent) from the header
21///
22/// Counterpart to [`ReadPropertyError`]
23#[derive(Clone, Copy, Debug, PartialEq)]
24pub enum ReadFitsKeyError {
25    /// Could not find a FITS key with the given name
26    NotFound,
27    /// Failed to parse the FITS key into the requested type
28    InvalidFormat,
29}
30impl Display for ReadFitsKeyError {
31    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
32        f.write_str("Failed to read FITS key: ")?;
33        match self {
34            &Self::NotFound => f.write_str("Key not found in header"),
35            &Self::InvalidFormat => f.write_str("Failed to parse key value"),
36        }
37    }
38}
39impl Error for ReadFitsKeyError {}
40
41/// A Failure to parse an [XISF Property](crate::property::PropertyContent)
42///
43/// Counterpart to [`ReadFitsKeyError`]
44#[derive(Clone, Copy, Debug, PartialEq)]
45pub enum ReadPropertyError {
46    /// Could not find an XISF property with the given id
47    NotFound,
48    /// Failed to parse the property into the requested type
49    InvalidFormat,
50}
51impl Display for ReadPropertyError {
52    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
53        f.write_str("Failed to read XISF property: ")?;
54        match self {
55            &Self::NotFound => f.write_str("ID not found"),
56            &Self::InvalidFormat => f.write_str("Failed to parse value"),
57        }
58    }
59}
60impl Error for ReadPropertyError {}
61
62/// A failure to either parse or resolve a `<Reference>` XML element
63///
64/// Enum variants are listed in order of precedence, e.g. if the parser encounters the node
65/// `<Reference uid="invalid uid" ref="invalid uid" />` (a recursive reference and an invalid uid)
66/// attempting to resolve the reference will return `InvalidUid` instead of `Recursive`
67#[derive(Clone, Copy, Debug, PartialEq)]
68pub enum ReferenceError {
69    /// The `ref` attribute is missing, so there isn't anything to search for
70    MissingRef,
71    /// The `ref` attribute contains an invalid uid
72    ///
73    /// Valid uids satisfy the regex `[_a-zA-Z][_a-zA-Z0-9]*`
74    InvalidUid,
75    /// No elements found with the requested uid
76    NotFound,
77    /// Reference to a reference
78    Recursive,
79    /// More than one element was found with the requested uid
80    Ambiguous,
81}
82impl Display for ReferenceError {
83    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
84        f.write_str("Invalid Reference element")
85    }
86}
87impl Error for ReferenceError {}
88
89///
90#[derive(Clone, Copy, Debug, PartialEq)]
91pub enum ParseNodeErrorKind {
92    /// Node
93    InvalidReference,
94    /// This element requires an attribute that was not present
95    MissingAttr,
96    /// An attribute on this element parsing or validation
97    InvalidAttr,
98    /// This element requires a child node that was not present
99    MissingChild,
100    /// not used in a general case, only for when the child itself doesn't return its own ParseNodeError, such as
101    /// the text nodes of embedded [`DataBlock`](crate::data_block::DataBlock)s
102    InvalidChild,
103}
104
105/// A failure to parse a [Core Element](https://pixinsight.com/doc/docs/XISF-1.0-spec/XISF-1.0-spec.html#__XISF_Core_Elements__)
106/// from the XML document making up an XISF header
107#[derive(Clone, Copy, Debug, PartialEq)]
108pub struct ParseNodeError {
109    /// The XML tag name of the element that could not be parsed
110    ///
111    /// E.g. if an `<Image ...>` element could not be parsed, `tag` will be "Image"
112    pub tag: &'static str,
113    /// What went wrong while trying to parse the element
114    pub kind: ParseNodeErrorKind,
115}
116impl ParseNodeError {
117    pub(crate) const fn new(tag: &'static str, kind: ParseNodeErrorKind) -> Self {
118        Self {
119            tag,
120            kind,
121        }
122    }
123}
124impl Display for ParseNodeError {
125    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
126        f.write_fmt(format_args!("Failed to parse <{}> node in XML header: ", self.tag))?;
127
128        use ParseNodeErrorKind::*;
129        match self.kind {
130            InvalidReference => f.write_str("Invalid child <Reference> node"),
131            MissingAttr => f.write_str("Missing attribute"),
132            InvalidAttr => f.write_str("Invalid Attribute"),
133            MissingChild => f.write_str("Missing child node"),
134            InvalidChild => f.write_str("Invalid child node"),
135        }
136    }
137}
138impl Error for ParseNodeError {}
139
140/// A failure to read bytes from a [`DataBlock`](crate::data_block::DataBlock)
141#[derive(Clone, Debug, PartialEq)]
142pub enum ReadDataBlockError {
143    /// Something went wrong while interacting with the underlying file or stream
144    IoError,
145    /// The data block location is not supported for the current file
146    ///
147    /// For example, trying to access a remote data block in a monolithic file,
148    /// or trying to access an attached data block in a distributed file.
149    UnsupportedLocation,
150    /// The checksum failed verification
151    DifferentChecksum,
152    ///
153    BadByteShuffleItemSize,
154    /// Failed to read an attached data block because the file is in use by another data block
155    ///
156    /// I'm pretty sure this is impossible to get with safe rust since [`Context`](crate::data_block::Context) doesn't `impl Send + Sync`,
157    /// and the file handle is released by the time the public API call finishes, but it's here just in case I'm wrong.
158    FileInUse,
159    /// Failed to read an inline/embedded data block
160    BadTextEncoding,
161    /// The host is not in the context's
162    ///
163    /// Ask the user if they trust the provided host, and if yes,
164    /// add it to the context with [`Context::trust_host`](crate::data_block::Context::trust_host), and try to read the block again
165    ///
166    /// <div class="warning">
167    /// This should not be done automatically, as a malicious server could permanently hang the reader.
168    /// Always ask before adding a trusted host.
169    /// </div>
170    UntrustedHost(Host),
171    /// Failed to authenticate with the remote host
172    ///
173    /// <div class="warning">
174    /// Some servers may enforce a cooldown period between attempts, so it's best to be proactive in asking for credentials before connecting.
175    /// Consider asking for credentials at the same time as when you ask the user to trust the host.
176    /// </div>
177    Unauthorized(Url),
178    /// Current supported schemes: `ftp`, `http`, `https`, `file`
179    UnsupportedScheme(String),
180    /// The URL for this data block is missing a remote host
181    MissingHost,
182}
183impl Display for ReadDataBlockError {
184    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
185        f.write_str("Failed to read XISF data block: ")?;
186        match self {
187            ReadDataBlockError::IoError => f.write_str("I/O Error"),
188            ReadDataBlockError::UnsupportedLocation => f.write_str("Data block location not supported for file type"),
189            ReadDataBlockError::DifferentChecksum => f.write_str("Checksum verification failed"),
190            ReadDataBlockError::BadByteShuffleItemSize => f.write_str("Uncompressed size is not divisible by byte shuffling item size"),
191            ReadDataBlockError::FileInUse => f.write_str("File handle is in use by another data block"),
192            ReadDataBlockError::BadTextEncoding => f.write_str("Failed to decode embedded or inline data block"),
193            ReadDataBlockError::UntrustedHost(host) => f.write_fmt(format_args!("Remote host {} is not trusted by user", host)),
194            ReadDataBlockError::Unauthorized(url) => f.write_fmt(format_args!("Access to remote resource at {} is unauthorized", url)),
195            ReadDataBlockError::UnsupportedScheme(scheme) => f.write_fmt(format_args!("URI scheme {} is not supported", scheme)),
196            ReadDataBlockError::MissingHost => f.write_str("Remote data block URI is missing a host"),
197        }
198    }
199}
200impl Error for ReadDataBlockError {}
201
202/// A failure to read an XISF file
203///
204/// Since data from XISF files are read lazily, a lack of `ReadFileError` does not guarantee that
205/// all data blocks can be read, or all FITS keys/XISF properties can be parsed, for example.
206#[derive(Clone, Copy, Debug, PartialEq)]
207pub struct ReadFileError;
208impl Display for ReadFileError {
209    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
210        f.write_str("Failed to read XISF file")
211    }
212}
213impl Error for ReadFileError {}
214
215/// A failure to convert a [`DynImageData`](crate::image::DynImageData)
216/// enum to one of its [`ImageData`](crate::image::ImageData) variants
217#[derive(Clone, Copy, Debug, PartialEq)]
218pub struct DowncastDynImageError(pub &'static str);
219impl Display for DowncastDynImageError {
220    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
221        f.write_fmt(format_args!("Failed to downcast DynImageData to type {}", self.0))
222    }
223}
224impl Error for DowncastDynImageError {}