openmassspec_io/error.rs
1//! Typed error for the `openmassspec-io` umbrella crate.
2//!
3//! Vendor reader errors are carried as boxed trait objects in
4//! [`Error::Reader`], so this type does not change when a reader crate
5//! releases a new major version.
6
7use std::path::PathBuf;
8
9/// A vendor reader's own error, type-erased.
10pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
11
12/// Errors produced by the umbrella `openmassspec-io` API.
13///
14/// Every failure that comes from a vendor reader, whether opening the
15/// input or decoding one spectrum or chromatogram, is an
16/// [`Error::Reader`] holding that reader's own error. Use
17/// [`Error::reader_error`] and `downcast_ref` when you need the reader's
18/// typed error; see the example there.
19///
20/// New variants may be added in minor releases; match with a wildcard arm.
21#[derive(Debug, thiserror::Error)]
22#[non_exhaustive]
23pub enum Error {
24 /// The path did not match any supported vendor signature.
25 #[error("unsupported vendor format at {0}")]
26 UnsupportedFormat(PathBuf),
27
28 /// A vendor format was detected but its feature was not enabled at
29 /// build time.
30 #[error("openmassspec-io was built without the '{vendor}' feature")]
31 FeatureDisabled {
32 /// Vendor name as returned by [`VendorFormat::name`](crate::VendorFormat::name).
33 vendor: &'static str,
34 },
35
36 /// A vendor reader could not open the input or decode a record.
37 #[error("{vendor} reader: {source}")]
38 #[non_exhaustive]
39 Reader {
40 /// Vendor name as returned by [`VendorFormat::name`](crate::VendorFormat::name).
41 vendor: &'static str,
42 /// The reader crate's own error (for example `opentfraw::Error`).
43 #[source]
44 source: BoxError,
45 },
46
47 /// Wrapping I/O error (creating or flushing the output, opening the
48 /// input file).
49 #[error(transparent)]
50 Io(#[from] std::io::Error),
51
52 /// Wrapping `openmassspec-core` error from the mzML writer or the
53 /// conformance harness. Reader errors that travel through core as
54 /// `openmassspec_core::Error::Vendor` are reported as
55 /// [`Error::Reader`] instead.
56 #[error(transparent)]
57 Core(openmassspec_core::Error),
58
59 /// Error reading an mzML file (used by the `vendor2mzml validate`
60 /// mzML path). A message rather than a typed source so this crate does
61 /// not depend on an mzML parser.
62 #[error("mzML error: {0}")]
63 Mzml(String),
64
65 /// Returned by a [`stream`](crate::stream)/[`stream_centroided`](crate::stream_centroided)
66 /// `on_spectrum` callback to stop iteration early - not a decode
67 /// failure, just a request to bail out (e.g. the consumer went away).
68 #[error("streaming was cancelled")]
69 Cancelled,
70}
71
72impl Error {
73 /// Wrap a vendor reader's error for `vendor` (a
74 /// [`VendorFormat::name`](crate::VendorFormat::name) value).
75 pub fn reader<E>(vendor: &'static str, err: E) -> Self
76 where
77 E: std::error::Error + Send + Sync + 'static,
78 {
79 Error::Reader {
80 vendor,
81 source: Box::new(err),
82 }
83 }
84
85 /// The vendor reader's own error, when this error came from a reader.
86 ///
87 /// Downcast it to the reader crate's error type to inspect
88 /// reader-specific detail. The reader crate must then be a direct
89 /// dependency of your crate; this crate does not re-export it.
90 ///
91 /// ```
92 /// use openmassspec_io::Error;
93 ///
94 /// // Stand-in for a reader crate's error type, e.g. `opentfraw::Error`.
95 /// #[derive(Debug)]
96 /// struct ScanTruncated(u32);
97 /// impl std::fmt::Display for ScanTruncated {
98 /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 /// write!(f, "scan {} is truncated", self.0)
100 /// }
101 /// }
102 /// impl std::error::Error for ScanTruncated {}
103 ///
104 /// let err = Error::reader("thermo", ScanTruncated(7));
105 /// match err.reader_error().and_then(|e| e.downcast_ref::<ScanTruncated>()) {
106 /// Some(ScanTruncated(scan)) => assert_eq!(*scan, 7),
107 /// None => unreachable!(),
108 /// }
109 /// ```
110 pub fn reader_error(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
111 match self {
112 Error::Reader { source, .. } => Some(source.as_ref()),
113 Error::Core(openmassspec_core::Error::Vendor(source)) => Some(source.as_ref()),
114 _ => None,
115 }
116 }
117
118 /// The vendor whose reader produced this error, if any.
119 pub fn reader_vendor(&self) -> Option<&'static str> {
120 match self {
121 Error::Reader { vendor, .. } => Some(vendor),
122 _ => None,
123 }
124 }
125
126 /// Attribute a reader error that arrived through core as
127 /// `openmassspec_core::Error::Vendor` to `vendor`.
128 pub(crate) fn attribute_to(self, vendor: &'static str) -> Self {
129 match self {
130 Error::Core(openmassspec_core::Error::Vendor(source)) => {
131 Error::Reader { vendor, source }
132 }
133 other => other,
134 }
135 }
136}
137
138impl From<openmassspec_core::Error> for Error {
139 fn from(e: openmassspec_core::Error) -> Self {
140 Error::Core(e)
141 }
142}
143
144/// Convenience alias mirroring the vendor crates.
145pub type Result<T> = std::result::Result<T, Error>;