Skip to main content

plist/
error.rs

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