plist/
error.rs

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