imzml/
error.rs

1use std::{
2    collections::VecDeque,
3    fmt::Display,
4    num::ParseIntError,
5    str::Utf8Error,
6    sync::{MutexGuard, PoisonError},
7};
8
9use base64::DecodeError;
10use serde::{ser::SerializeStruct, Serialize, Serializer};
11
12use crate::{
13    mzml::{attributes::AttributeType, Tag},
14    obo::ValueType,
15};
16
17/// Possible errors occuring during the parsing of an (i)mzML file, which do not prevent further parsing
18#[derive(Debug)]
19pub enum ParseError {
20    /// Error occured when processing an integer at the current `Tag`
21    IntError((Tag, ParseIntError)),
22    /// Error occured when parsing UTF8 (typically String values)
23    Utf8Error((Tag, Utf8Error)),
24    /// General XML error occured when parsing the file (`quick_xml::Error`)
25    XMLError((Tag, quick_xml::Error)),
26    /// A attribute was included on the `Tag` that was not expected (not in the specification)
27    UnexpectedAttribute((Tag, String)),
28    /// A attribute was not included on the `Tag` that was expected (listed as required in the specification)
29    MissingAttribute((Tag, String)),
30    /// The value of an attribute is missing (i.e. <tag attribute="">) where one is expected
31    MissingAttributeValue((Tag, String, AttributeType)),
32    /// A XML tag was encountered that was not expected (not in the specification)
33    UnexpectedTag(String),
34    /// Reference in the specified `Tag` cannot be found in the (i)mzML document
35    MissingRef {
36        /// Location within the file
37        breadcrumbs: Breadcrumbs,
38        /// Tag which contains the 'ref' attribute
39        tag: Tag,
40        /// Tag being referenced
41        ref_to_tag: Tag,
42        /// The missing ID in the 'ref' attribute
43        ref_id: String,
44    },
45    /// Value in CV param missing, but required
46    MissingValue((Breadcrumbs, String, ValueType)),
47    /// Value in CV param present, but shouldn't have one
48    UnexpectedValue((Breadcrumbs, String, String)),
49    /// Invalid value type present in CV param (cannot convert value present to the expected value type)
50    InvalidValue((Breadcrumbs, String, String)),
51
52    /// Unknown accession included as cvParam (not present in the specified ontology)
53    UnknownAccession((Breadcrumbs, String)),
54    // //Text(String),
55    // //Unknown,
56
57    // From old parser
58    //UnimplementedTag(String),
59    //UnexpectedParent((Tag, Tag)),
60}
61
62impl ParseError {
63    /// Returns a description of the error
64    pub fn description(&self) -> String {
65        match self {
66            ParseError::IntError((tag, int_error)) => {
67                format!("Couldn't convert integer at tag {}: {}", tag, int_error)
68            }
69            ParseError::Utf8Error(_) => todo!(),
70            ParseError::XMLError(_) => todo!(),
71            ParseError::UnexpectedAttribute((tag, string)) => {
72                format!("{} tag has an unexpected attribute {}", tag, string)
73            }
74            ParseError::MissingAttribute((tag, string)) => {
75                format!("{} tag is missing required attribute. {}", tag, string)
76            }
77            ParseError::MissingAttributeValue((tag, attribute, attribute_type)) => {
78                format!(
79                    "{} tag is missing value  of type {:?} for attribute {}",
80                    tag, attribute_type, attribute
81                )
82            }
83            ParseError::UnexpectedTag(string) => {
84                format!("Unexpected tag: {}", string)
85            }
86            ParseError::MissingRef {
87                breadcrumbs: _breadcrumbs,
88                tag,
89                ref_to_tag,
90                ref_id,
91            } => {
92                format!(
93                    "Tag {} includes a reference to a {} tag with id {}, but this doesn't exist",
94                    tag, ref_to_tag, ref_id
95                )
96            }
97            ParseError::MissingValue((_breadcrumbs, data, value_type)) => {
98                format!(
99                    " Missing value of type {:?} for cvParam {}",
100                    value_type, data
101                )
102            }
103            ParseError::InvalidValue((_breadcrumbs, data, value)) => {
104                format!("Found an invalid value ({}) for cvParam {}", value, data)
105            }
106            ParseError::UnexpectedValue((_breadcrumbs, data, value)) => {
107                format!(
108                    "Found a value ({}) when one wasn't expected for cvParam {}",
109                    value, data
110                )
111            }
112            ParseError::UnknownAccession((_breadcrumbs, accession)) => {
113                format!(
114                    "Found an accession (cvParam) ({}) that was not present in the ontology",
115                    accession
116                )
117            }
118        }
119    }
120}
121
122impl Display for ParseError {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        match self {
125            ParseError::IntError(_) => {
126                write!(f, "{}", self.description())
127            }
128            ParseError::Utf8Error(_) => {
129                write!(f, "{}", self.description())
130            },
131            ParseError::XMLError(_) => {
132                write!(f, "{}", self.description())
133            },
134            ParseError::UnexpectedAttribute(_) => {
135                write!(f, "{}", self.description())
136            }
137            ParseError::MissingAttribute(_) => {
138                write!(f, "{}", self.description())
139            }
140            ParseError::MissingAttributeValue(_) => {
141                write!(f, "{}", self.description())
142            }
143            ParseError::UnexpectedTag(_) => {
144                write!(f, "{}", self.description())
145            }
146            ParseError::MissingRef {
147                breadcrumbs,
148                tag: _,
149                ref_to_tag: _,
150                ref_id: _,
151            } => {
152                write!(
153                    f,
154                    "[{}] {}",
155                    breadcrumbs, self.description()
156                )
157            }
158            ParseError::MissingValue((breadcrumbs, _data, _value_type)) => {
159                write!(
160                    f,
161                    "[{}] {}",
162                    breadcrumbs, self.description()
163                )
164            }
165            ParseError::InvalidValue((breadcrumbs, _data, _value)) => {
166                write!(
167                    f,
168                    "[{}] {}",
169                    breadcrumbs, self.description()
170                )
171            }
172            ParseError::UnexpectedValue((breadcrumbs, _data, _value)) => {
173                write!(
174                    f,
175                    "[{}] {}",
176                    breadcrumbs, self.description()
177                )
178            }
179            ParseError::UnknownAccession((breadcrumbs, _accession)) => {
180                write!(
181                    f,
182                    "[{}] {}",
183                    breadcrumbs, self.description()
184                )
185            }
186
187            // From old parser
188            //ParseError::UnimplementedTag(_) => todo!(),
189            //ParseError::UnexpectedParent(_) => todo!(),
190        }
191    }
192}
193
194impl Serialize for ParseError {
195    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
196    where
197        S: Serializer,
198    {
199        let mut state = serializer.serialize_struct("parse_error", 2)?;
200
201        match self {
202            ParseError::IntError((tag, _error)) => {
203                state.serialize_field("type", "IntError")?;
204                state.serialize_field("tag", tag)?;
205                state.serialize_field("description", &self.description())?;
206            }
207            ParseError::Utf8Error((tag, _error)) => {
208                state.serialize_field("type", "Utf8Error")?;
209                state.serialize_field("tag", tag)?;
210                state.serialize_field("description", &self.description())?;
211            }
212            ParseError::XMLError((tag, _error)) => {
213                state.serialize_field("type", "XMLError")?;
214                state.serialize_field("tag", tag)?;
215                state.serialize_field("description", &self.description())?;
216            }
217            ParseError::UnexpectedAttribute((tag, _error)) => {
218                state.serialize_field("type", "UnexpectedAttribute")?;
219                state.serialize_field("tag", tag)?;
220                state.serialize_field("description", &self.description())?;
221            }
222            ParseError::MissingAttribute((tag, _error)) => {
223                state.serialize_field("type", "MissingAttribute")?;
224                state.serialize_field("tag", tag)?;
225                state.serialize_field("description", &self.description())?;
226            }
227            ParseError::MissingAttributeValue((tag, _attribute, _attribute_value)) => {
228                state.serialize_field("type", "MissingAttributeValue")?;
229                state.serialize_field("tag", tag)?;
230                state.serialize_field("description", &self.description())?;
231            }
232            ParseError::UnexpectedTag(_) => {
233                state.serialize_field("type", "UnexpectedTag")?;
234                state.serialize_field("description", &self.description())?;
235            }
236            ParseError::MissingRef {
237                breadcrumbs,
238                tag,
239                ref_to_tag,
240                ref_id,
241            } => {
242                state.serialize_field("type", "MissingRef")?;
243                state.serialize_field("tag", tag)?;
244                state.serialize_field("description", &self.description())?;
245                state.serialize_field("location", &format!("{}", breadcrumbs))?;
246                state.serialize_field("ref_to_tag", ref_to_tag)?;
247                state.serialize_field("ref_id", ref_id)?;
248            }
249            ParseError::MissingValue((breadcrumbs, _details, value_type)) => {
250                state.serialize_field("type", "MissingValue")?;
251                state.serialize_field("description", &self.description())?;
252                state.serialize_field("location", &format!("{}", breadcrumbs))?;
253                state.serialize_field("expected_value_type", value_type)?;
254            }
255            ParseError::UnexpectedValue((breadcrumbs, _, _)) => {
256                state.serialize_field("type", "UnexpectedValue")?;
257                state.serialize_field("description", &self.description())?;
258                state.serialize_field("location", &format!("{}", breadcrumbs))?;
259            }
260            ParseError::InvalidValue((breadcrumbs, _, _)) => {
261                state.serialize_field("type", "InvalidValue")?;
262                state.serialize_field("description", &self.description())?;
263                state.serialize_field("location", &format!("{}", breadcrumbs))?;
264            }
265            ParseError::UnknownAccession((breadcrumbs, _)) => {
266                state.serialize_field("type", "UnknownAccession")?;
267                state.serialize_field("description", &self.description())?;
268                state.serialize_field("location", &format!("{}", breadcrumbs))?;
269            }
270        }
271
272        state.end()
273    }
274}
275
276/// Keep track of tag hierarchy to aid in error reporting.
277#[derive(Debug, Clone)]
278pub struct Breadcrumbs(pub VecDeque<(Tag, Option<String>)>);
279
280impl std::fmt::Display for Breadcrumbs {
281    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282        for (index, (tag, id)) in self.0.iter().enumerate() {
283            if index > 0 {
284                write!(f, ">")?;
285            }
286
287            write!(f, "{}", tag)?;
288
289            if let Some(id) = id {
290                write!(f, "[{}]", id)?;
291            }
292        }
293
294        Ok(())
295    }
296}
297
298/// Possible errors occuring during the parsing of an (i)mzML file, which prevent further parsing
299#[non_exhaustive]
300#[derive(Debug)]
301pub enum FatalParseError {
302    /// I/O error when interacting with the file
303    IOError(std::io::Error),
304    /// Issue with the validity of the XML causing parsing to cease
305    XMLError(quick_xml::Error),
306    /// Expected a different tag, so unable to proceed
307    UnexpectedTag(String),
308    //UnimplementedTag(String),
309    /// Tag is not imlpemented correctly (e.g. missing attributes)
310    InvalidTag((Tag, String)),
311    /// An expected opening tag is missing
312    MissingOpeningTag(String),
313    /// An expected closing tag is missing
314    MissingClosingTag(String),
315    /// an unexpected error has occured
316    UnexpectedError(String),
317    /// Invalid UTF-8 found when trying to convert [u8] to UTF8
318    InvalidUtf8(Utf8Error),
319    /// An unexpected XML event has been encountered
320    UnexpectedEvent(String),
321}
322
323impl Serialize for FatalParseError {
324    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
325    where
326        S: Serializer,
327    {
328        let mut state = serializer.serialize_struct("fatal_error", 2)?;
329
330        match self {
331            FatalParseError::IOError(error) => {
332                state.serialize_field("type", "IOError")?;
333                state.serialize_field("details", &format!("{}", error))?;
334            }
335            FatalParseError::XMLError(error) => {
336                state.serialize_field("type", "XMLError")?;
337                state.serialize_field("details", &format!("{}", error))?;
338            }
339            FatalParseError::UnexpectedTag(error) => {
340                state.serialize_field("type", "UnexpectedTag")?;
341                state.serialize_field("details", error)?;
342            }
343            FatalParseError::MissingOpeningTag(error) => {
344                state.serialize_field("type", "MissingOpeningTag")?;
345                state.serialize_field("details", error)?;
346            }
347            FatalParseError::MissingClosingTag(error) => {
348                state.serialize_field("type", "MissingOpeningTag")?;
349                state.serialize_field("details", error)?;
350            }
351            FatalParseError::UnexpectedError(error) => {
352                state.serialize_field("type", "UnexpectedError")?;
353                state.serialize_field("details", error)?;
354            }
355            FatalParseError::UnexpectedEvent(error) => {
356                state.serialize_field("type", "UnexpectedEvent")?;
357                state.serialize_field("details", error)?;
358            }
359            FatalParseError::InvalidUtf8(error) => {
360                state.serialize_field("type", "InvalidUtf8")?;
361                state.serialize_field("details", &format!("{:?}", error))?;
362            }
363            FatalParseError::InvalidTag((tag, error)) => {
364                state.serialize_field("type", "InvalidTag")?;
365                state.serialize_field("tag", &format!("{}", tag))?;
366                state.serialize_field("details", error)?;
367            }
368        }
369
370        state.end()
371    }
372}
373
374impl From<std::io::Error> for FatalParseError {
375    fn from(error: std::io::Error) -> Self {
376        FatalParseError::IOError(error)
377    }
378}
379
380impl From<Utf8Error> for FatalParseError {
381    fn from(error: Utf8Error) -> Self {
382        FatalParseError::InvalidUtf8(error)
383    }
384}
385
386impl From<quick_xml::Error> for FatalParseError {
387    fn from(error: quick_xml::Error) -> Self {
388        FatalParseError::XMLError(error)
389    }
390}
391
392/// Error occured when accessing and/or decoding data
393#[derive(Debug)]
394pub enum DataError {
395    /// Mutex was poisoned and so cannot continue
396    MutexError,
397    /// I/O error occured when interacting with the file
398    IOError(std::io::Error),
399    /// Base64 encoding is invalid
400    Base64DecodeError(DecodeError),
401    /// Unknown or unsupported data type specified in the metadata, not sure how to proceed
402    UnknownDataType,
403}
404
405impl<'a, D> From<PoisonError<MutexGuard<'a, D>>> for DataError {
406    fn from(_poison_error: PoisonError<MutexGuard<'a, D>>) -> Self {
407        Self::MutexError
408    }
409}
410
411impl From<std::io::Error> for DataError {
412    fn from(io_error: std::io::Error) -> Self {
413        Self::IOError(io_error)
414    }
415}
416
417impl From<DecodeError> for DataError {
418    fn from(decode_error: DecodeError) -> Self {
419        Self::Base64DecodeError(decode_error)
420    }
421}