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 {}