creator_plist/
error.rs

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