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}