error.rs

  1/*!
  2# Error types for XML parsing
  3
  4This module contains the error types used throughout the `xso` crate.
  5*/
  6
  7// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
  8//
  9// This Source Code Form is subject to the terms of the Mozilla Public
 10// License, v. 2.0. If a copy of the MPL was not distributed with this
 11// file, You can obtain one at http://mozilla.org/MPL/2.0/.
 12use core::fmt;
 13
 14use rxml::Error as XmlError;
 15
 16/// Opaque string error.
 17///
 18/// This is exclusively used in the `From<&Error> for Error` implementation
 19/// in order to type-erase and "clone" the TextParseError.
 20///
 21/// That implementation, in turn, is primarily used by the
 22/// `AsXml for Result<T, E>` implementation. We intentionally do not implement
 23/// `Clone` using this type because it'd lose type information (which you
 24/// don't expect a clone to do).
 25#[derive(Debug)]
 26struct OpaqueError(String);
 27
 28impl fmt::Display for OpaqueError {
 29    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 30        f.write_str(&self.0)
 31    }
 32}
 33
 34impl std::error::Error for OpaqueError {}
 35
 36/// Error variants generated while parsing or serialising XML data.
 37#[derive(Debug)]
 38pub enum Error {
 39    /// Invalid XML data encountered
 40    XmlError(XmlError),
 41
 42    /// Attempt to parse text data failed with the provided nested error.
 43    TextParseError(Box<dyn std::error::Error + Send + Sync + 'static>),
 44
 45    /// Generic, unspecified other error.
 46    Other(&'static str),
 47
 48    /// An element header did not match an expected element.
 49    ///
 50    /// This is only rarely generated: most of the time, a mismatch of element
 51    /// types is reported as either an unexpected or a missing child element,
 52    /// errors which are generally more specific.
 53    TypeMismatch,
 54}
 55
 56impl Error {
 57    /// Convenience function to create a [`Self::TextParseError`] variant.
 58    ///
 59    /// This includes the `Box::new(.)` call, making it directly usable as
 60    /// argument to [`Result::map_err`].
 61    pub fn text_parse_error<T: std::error::Error + Send + Sync + 'static>(e: T) -> Self {
 62        Self::TextParseError(Box::new(e))
 63    }
 64}
 65
 66/// "Clone" an [`Error`] while discarding some information.
 67///
 68/// This discards the specific type information from the
 69/// [`TextParseError`][`Self::TextParseError`] variant and it may discard
 70/// more information in the future.
 71impl From<&Error> for Error {
 72    fn from(other: &Error) -> Self {
 73        match other {
 74            Self::XmlError(e) => Self::XmlError(e.clone()),
 75            Self::TextParseError(e) => Self::TextParseError(Box::new(OpaqueError(e.to_string()))),
 76            Self::Other(e) => Self::Other(e),
 77            Self::TypeMismatch => Self::TypeMismatch,
 78        }
 79    }
 80}
 81
 82impl fmt::Display for Error {
 83    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 84        match self {
 85            Self::XmlError(ref e) => write!(f, "xml parse error: {}", e),
 86            Self::TextParseError(ref e) => write!(f, "text parse error: {}", e),
 87            Self::TypeMismatch => f.write_str("mismatch between expected and actual XML data"),
 88            Self::Other(msg) => f.write_str(msg),
 89        }
 90    }
 91}
 92
 93impl std::error::Error for Error {
 94    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
 95        match self {
 96            Self::XmlError(ref e) => Some(e),
 97            Self::TextParseError(ref e) => Some(&**e),
 98            _ => None,
 99        }
100    }
101}
102
103impl From<rxml::Error> for Error {
104    fn from(other: rxml::Error) -> Error {
105        Error::XmlError(other)
106    }
107}
108
109impl From<rxml::strings::Error> for Error {
110    fn from(other: rxml::strings::Error) -> Error {
111        Error::XmlError(other.into())
112    }
113}
114
115impl From<core::convert::Infallible> for Error {
116    fn from(other: core::convert::Infallible) -> Self {
117        match other {}
118    }
119}
120
121/// Error returned from
122/// [`FromXml::from_events`][`crate::FromXml::from_events`].
123#[derive(Debug)]
124pub enum FromEventsError {
125    /// The `name` and/or `attrs` passed to `FromXml::from_events` did not
126    /// match the element's type.
127    Mismatch {
128        /// The `name` passed to `from_events`.
129        name: rxml::QName,
130
131        /// The `attrs` passed to `from_events`.
132        attrs: rxml::AttrMap,
133    },
134
135    /// The `name` and `attrs` passed to `FromXml::from_events` matched the
136    /// element's type, but the data was invalid. Details are in the inner
137    /// error.
138    Invalid(Error),
139}
140
141impl From<Error> for FromEventsError {
142    fn from(other: Error) -> Self {
143        Self::Invalid(other)
144    }
145}
146
147impl From<core::convert::Infallible> for FromEventsError {
148    fn from(other: core::convert::Infallible) -> Self {
149        match other {}
150    }
151}
152
153impl fmt::Display for FromEventsError {
154    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
155        match self {
156            Self::Mismatch { .. } => f.write_str("element header did not match"),
157            Self::Invalid(ref e) => fmt::Display::fmt(e, f),
158        }
159    }
160}
161
162impl std::error::Error for FromEventsError {
163    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
164        match self {
165            Self::Mismatch { .. } => None,
166            Self::Invalid(ref e) => Some(e),
167        }
168    }
169}
170
171impl From<Error> for Result<minidom::Element, Error> {
172    fn from(other: Error) -> Self {
173        Self::Err(other)
174    }
175}
176
177/// Error returned by the `TryFrom<Element>` implementations.
178#[derive(Debug)]
179pub enum FromElementError {
180    /// The XML element header did not match the expectations of the type
181    /// implementing `TryFrom`.
182    ///
183    /// Contains the original `Element` unmodified.
184    Mismatch(minidom::Element),
185
186    /// During processing of the element, an (unrecoverable) error occured.
187    Invalid(Error),
188}
189
190impl fmt::Display for FromElementError {
191    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192        match self {
193            Self::Mismatch(ref el) => write!(
194                f,
195                "expected different XML element (got {} in namespace {})",
196                el.name(),
197                el.ns()
198            ),
199            Self::Invalid(ref e) => fmt::Display::fmt(e, f),
200        }
201    }
202}
203
204impl std::error::Error for FromElementError {
205    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
206        match self {
207            Self::Mismatch(_) => None,
208            Self::Invalid(ref e) => Some(e),
209        }
210    }
211}
212
213impl From<Result<minidom::Element, Error>> for FromElementError {
214    fn from(other: Result<minidom::Element, Error>) -> Self {
215        match other {
216            Ok(v) => Self::Mismatch(v),
217            Err(e) => Self::Invalid(e),
218        }
219    }
220}
221
222impl From<Error> for FromElementError {
223    fn from(other: Error) -> Self {
224        Self::Invalid(other)
225    }
226}
227
228impl From<FromElementError> for Error {
229    fn from(other: FromElementError) -> Self {
230        match other {
231            FromElementError::Invalid(e) => e,
232            FromElementError::Mismatch(..) => Self::TypeMismatch,
233        }
234    }
235}
236
237impl From<core::convert::Infallible> for FromElementError {
238    fn from(other: core::convert::Infallible) -> Self {
239        match other {}
240    }
241}