1use std::{error, fmt, io};
2use quick_xml::escape::EscapeError;
3use quick_xml::encoding::EncodingError;
4
5#[cfg(feature = "serde")]
6use crate::stream::Event;
7use crate::{InvalidXmlDate, Value};
8
9#[derive(Debug)]
11pub struct Error {
12 inner: Box<ErrorImpl>,
13}
14
15#[derive(Debug)]
16pub(crate) struct ErrorImpl {
17 kind: ErrorKind,
18 file_position: Option<FilePosition>,
19}
20
21#[derive(Debug)]
22pub(crate) enum ErrorKind {
23 UnexpectedEof,
24 UnexpectedEndOfEventStream,
25 UnexpectedEventType {
26 #[allow(dead_code)]
28 expected: EventKind,
29 #[allow(dead_code)]
30 found: EventKind,
31 },
32 ExpectedEndOfEventStream {
33 #[allow(dead_code)]
35 found: EventKind,
36 },
37
38 UnclosedString,
40 IncompleteComment,
41 InvalidUtf8AsciiStream,
42 InvalidOctalString,
43
44 UnclosedXmlElement,
46 UnexpectedXmlCharactersExpectedElement,
47 UnexpectedXmlOpeningTag,
48 UnknownXmlElement,
49 InvalidXmlSyntax,
50 InvalidXmlUtf8,
51 InvalidDataString,
52 InvalidDateString,
53 InvalidIntegerString,
54 InvalidRealString,
55 UidNotSupportedInXmlPlist,
56
57 ObjectTooLarge,
59 InvalidMagic,
60 InvalidTrailerObjectOffsetSize, InvalidTrailerObjectReferenceSize, InvalidObjectLength,
63 ObjectReferenceTooLarge,
64 ObjectOffsetTooLarge,
65 RecursiveObject,
66 NullObjectUnimplemented,
67 FillObjectUnimplemented,
68 IntegerOutOfRange,
69 InfiniteOrNanDate,
70 InvalidUtf8String,
71 InvalidUtf16String,
72 UnknownObjectType(
73 #[allow(dead_code)] u8,
75 ),
76
77 Io(io::Error),
78 #[cfg(feature = "serde")]
79 Serde(
80 #[allow(dead_code)] String,
82 ),
83}
84
85#[derive(Debug, Clone, Copy)]
86pub(crate) struct FilePosition(pub(crate) u64);
87
88#[derive(Copy, Clone, PartialEq, Eq, Debug)]
89pub(crate) enum EventKind {
90 StartArray,
91 StartDictionary,
92 EndCollection,
93 Boolean,
94 Data,
95 Date,
96 Integer,
97 Real,
98 String,
99 Uid,
100
101 ValueOrStartCollection,
102 DictionaryKeyOrEndCollection,
103}
104
105impl Error {
106 pub fn is_io(&self) -> bool {
108 self.as_io().is_some()
109 }
110
111 pub fn is_eof(&self) -> bool {
113 matches!(self.inner.kind, ErrorKind::UnexpectedEof)
114 }
115
116 pub fn as_io(&self) -> Option<&io::Error> {
119 if let ErrorKind::Io(err) = &self.inner.kind {
120 Some(err)
121 } else {
122 None
123 }
124 }
125
126 pub fn into_io(self) -> Result<io::Error, Self> {
129 if let ErrorKind::Io(err) = self.inner.kind {
130 Ok(err)
131 } else {
132 Err(self)
133 }
134 }
135}
136
137impl error::Error for Error {
138 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
139 match &self.inner.kind {
140 ErrorKind::Io(err) => Some(err),
141 _ => None,
142 }
143 }
144}
145
146impl fmt::Display for Error {
147 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148 if let Some(position) = &self.inner.file_position {
149 write!(f, "{:?} ({})", &self.inner.kind, position)
150 } else {
151 fmt::Debug::fmt(&self.inner.kind, f)
152 }
153 }
154}
155
156impl fmt::Display for FilePosition {
157 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158 write!(f, "offset {}", self.0)
159 }
160}
161
162impl From<InvalidXmlDate> for Error {
163 fn from(error: InvalidXmlDate) -> Self {
164 ErrorKind::from(error).without_position()
165 }
166}
167
168impl ErrorKind {
169 pub fn with_byte_offset(self, offset: u64) -> Error {
170 self.with_position(FilePosition(offset))
171 }
172
173 pub fn with_position(self, pos: FilePosition) -> Error {
174 Error {
175 inner: Box::new(ErrorImpl {
176 kind: self,
177 file_position: Some(pos),
178 }),
179 }
180 }
181
182 pub fn without_position(self) -> Error {
183 Error {
184 inner: Box::new(ErrorImpl {
185 kind: self,
186 file_position: None,
187 }),
188 }
189 }
190}
191
192impl From<InvalidXmlDate> for ErrorKind {
193 fn from(_: InvalidXmlDate) -> Self {
194 ErrorKind::InvalidDateString
195 }
196}
197
198impl From<EscapeError> for ErrorKind {
199 fn from(_: EscapeError) -> Self {
200 ErrorKind::InvalidXmlUtf8
201 }
202}
203
204impl From<EncodingError> for ErrorKind {
205 fn from(_: EncodingError) -> Self {
206 ErrorKind::InvalidXmlUtf8
207 }
208}
209
210impl EventKind {
211 #[cfg(feature = "serde")]
212 pub fn of_event(event: &Event) -> EventKind {
213 match event {
214 Event::StartArray(_) => EventKind::StartArray,
215 Event::StartDictionary(_) => EventKind::StartDictionary,
216 Event::EndCollection => EventKind::EndCollection,
217 Event::Boolean(_) => EventKind::Boolean,
218 Event::Data(_) => EventKind::Data,
219 Event::Date(_) => EventKind::Date,
220 Event::Integer(_) => EventKind::Integer,
221 Event::Real(_) => EventKind::Real,
222 Event::String(_) => EventKind::String,
223 Event::Uid(_) => EventKind::Uid,
224 }
225 }
226
227 pub fn of_value(event: &Value) -> EventKind {
228 match event {
229 Value::Array(_) => EventKind::StartArray,
230 Value::Dictionary(_) => EventKind::StartDictionary,
231 Value::Boolean(_) => EventKind::Boolean,
232 Value::Data(_) => EventKind::Data,
233 Value::Date(_) => EventKind::Date,
234 Value::Integer(_) => EventKind::Integer,
235 Value::Real(_) => EventKind::Real,
236 Value::String(_) => EventKind::String,
237 Value::Uid(_) => EventKind::Uid,
238 }
239 }
240}
241
242impl fmt::Display for EventKind {
243 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
244 match self {
245 EventKind::StartArray => "StartArray",
246 EventKind::StartDictionary => "StartDictionary",
247 EventKind::EndCollection => "EndCollection",
248 EventKind::Boolean => "Boolean",
249 EventKind::Data => "Data",
250 EventKind::Date => "Date",
251 EventKind::Integer => "Integer",
252 EventKind::Real => "Real",
253 EventKind::String => "String",
254 EventKind::Uid => "Uid",
255 EventKind::ValueOrStartCollection => "value or start collection",
256 EventKind::DictionaryKeyOrEndCollection => "dictionary key or end collection",
257 }
258 .fmt(f)
259 }
260}
261
262pub(crate) fn from_io_without_position(err: io::Error) -> Error {
263 ErrorKind::Io(err).without_position()
264}
265
266#[cfg(feature = "serde")]
267pub(crate) fn unexpected_event_type(expected: EventKind, found: &Event) -> Error {
268 let found = EventKind::of_event(found);
269 ErrorKind::UnexpectedEventType { expected, found }.without_position()
270}