1 use crate::libyaml::{emitter, error as libyaml};
2 use crate::path::Path;
3 use serde::{de, ser};
4 use std::error::Error as StdError;
5 use std::fmt::{self, Debug, Display};
6 use std::io;
7 use std::result;
8 use std::string;
9 use std::sync::Arc;
10 
11 /// An error that happened serializing or deserializing YAML data.
12 pub struct Error(Box<ErrorImpl>);
13 
14 /// Alias for a `Result` with the error type `serde_yaml::Error`.
15 pub type Result<T> = result::Result<T, Error>;
16 
17 #[derive(Debug)]
18 pub(crate) enum ErrorImpl {
19     Message(String, Option<Pos>),
20 
21     Libyaml(libyaml::Error),
22     Io(io::Error),
23     FromUtf8(string::FromUtf8Error),
24 
25     EndOfStream,
26     MoreThanOneDocument,
27     RecursionLimitExceeded(libyaml::Mark),
28     RepetitionLimitExceeded,
29     BytesUnsupported,
30     UnknownAnchor(libyaml::Mark),
31     SerializeNestedEnum,
32     ScalarInMerge,
33     TaggedInMerge,
34     ScalarInMergeElement,
35     SequenceInMergeElement,
36     EmptyTag,
37     FailedToParseNumber,
38 
39     Shared(Arc<ErrorImpl>),
40 }
41 
42 #[derive(Debug)]
43 pub(crate) struct Pos {
44     mark: libyaml::Mark,
45     path: String,
46 }
47 
48 /// The input location that an error occured.
49 #[derive(Debug)]
50 pub struct Location {
51     index: usize,
52     line: usize,
53     column: usize,
54 }
55 
56 impl Location {
57     /// The byte index of the error
index(&self) -> usize58     pub fn index(&self) -> usize {
59         self.index
60     }
61 
62     /// The line of the error
line(&self) -> usize63     pub fn line(&self) -> usize {
64         self.line
65     }
66 
67     /// The column of the error
column(&self) -> usize68     pub fn column(&self) -> usize {
69         self.column
70     }
71 
72     // This is to keep decoupled with the yaml crate
73     #[doc(hidden)]
from_mark(mark: libyaml::Mark) -> Self74     fn from_mark(mark: libyaml::Mark) -> Self {
75         Location {
76             index: mark.index() as usize,
77             // `line` and `column` returned from libyaml are 0-indexed but all error messages add +1 to this value
78             line: mark.line() as usize + 1,
79             column: mark.column() as usize + 1,
80         }
81     }
82 }
83 
84 impl Error {
85     /// Returns the Location from the error if one exists.
86     ///
87     /// Not all types of errors have a location so this can return `None`.
88     ///
89     /// # Examples
90     ///
91     /// ```
92     /// # use serde_yaml::{Value, Error};
93     /// #
94     /// // The `@` character as the first character makes this invalid yaml
95     /// let invalid_yaml: Result<Value, Error> = serde_yaml::from_str("@invalid_yaml");
96     ///
97     /// let location = invalid_yaml.unwrap_err().location().unwrap();
98     ///
99     /// assert_eq!(location.line(), 1);
100     /// assert_eq!(location.column(), 1);
101     /// ```
location(&self) -> Option<Location>102     pub fn location(&self) -> Option<Location> {
103         self.0.location()
104     }
105 }
106 
new(inner: ErrorImpl) -> Error107 pub(crate) fn new(inner: ErrorImpl) -> Error {
108     Error(Box::new(inner))
109 }
110 
shared(shared: Arc<ErrorImpl>) -> Error111 pub(crate) fn shared(shared: Arc<ErrorImpl>) -> Error {
112     Error(Box::new(ErrorImpl::Shared(shared)))
113 }
114 
fix_mark(mut error: Error, mark: libyaml::Mark, path: Path) -> Error115 pub(crate) fn fix_mark(mut error: Error, mark: libyaml::Mark, path: Path) -> Error {
116     if let ErrorImpl::Message(_, none @ None) = error.0.as_mut() {
117         *none = Some(Pos {
118             mark,
119             path: path.to_string(),
120         });
121     }
122     error
123 }
124 
125 impl Error {
shared(self) -> Arc<ErrorImpl>126     pub(crate) fn shared(self) -> Arc<ErrorImpl> {
127         if let ErrorImpl::Shared(err) = *self.0 {
128             err
129         } else {
130             Arc::from(self.0)
131         }
132     }
133 }
134 
135 impl From<libyaml::Error> for Error {
from(err: libyaml::Error) -> Self136     fn from(err: libyaml::Error) -> Self {
137         Error(Box::new(ErrorImpl::Libyaml(err)))
138     }
139 }
140 
141 impl From<emitter::Error> for Error {
from(err: emitter::Error) -> Self142     fn from(err: emitter::Error) -> Self {
143         match err {
144             emitter::Error::Libyaml(err) => Self::from(err),
145             emitter::Error::Io(err) => new(ErrorImpl::Io(err)),
146         }
147     }
148 }
149 
150 impl StdError for Error {
source(&self) -> Option<&(dyn StdError + 'static)>151     fn source(&self) -> Option<&(dyn StdError + 'static)> {
152         self.0.source()
153     }
154 }
155 
156 impl Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result157     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158         self.0.display(f)
159     }
160 }
161 
162 // Remove two layers of verbosity from the debug representation. Humans often
163 // end up seeing this representation because it is what unwrap() shows.
164 impl Debug for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result165     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
166         self.0.debug(f)
167     }
168 }
169 
170 impl ser::Error for Error {
custom<T: Display>(msg: T) -> Self171     fn custom<T: Display>(msg: T) -> Self {
172         Error(Box::new(ErrorImpl::Message(msg.to_string(), None)))
173     }
174 }
175 
176 impl de::Error for Error {
custom<T: Display>(msg: T) -> Self177     fn custom<T: Display>(msg: T) -> Self {
178         Error(Box::new(ErrorImpl::Message(msg.to_string(), None)))
179     }
180 }
181 
182 impl ErrorImpl {
location(&self) -> Option<Location>183     fn location(&self) -> Option<Location> {
184         self.mark().map(Location::from_mark)
185     }
186 
source(&self) -> Option<&(dyn StdError + 'static)>187     fn source(&self) -> Option<&(dyn StdError + 'static)> {
188         match self {
189             ErrorImpl::Io(err) => err.source(),
190             ErrorImpl::FromUtf8(err) => err.source(),
191             ErrorImpl::Shared(err) => err.source(),
192             _ => None,
193         }
194     }
195 
mark(&self) -> Option<libyaml::Mark>196     fn mark(&self) -> Option<libyaml::Mark> {
197         match self {
198             ErrorImpl::Message(_, Some(Pos { mark, path: _ }))
199             | ErrorImpl::RecursionLimitExceeded(mark)
200             | ErrorImpl::UnknownAnchor(mark) => Some(*mark),
201             ErrorImpl::Libyaml(err) => Some(err.mark()),
202             ErrorImpl::Shared(err) => err.mark(),
203             _ => None,
204         }
205     }
206 
message_no_mark(&self, f: &mut fmt::Formatter) -> fmt::Result207     fn message_no_mark(&self, f: &mut fmt::Formatter) -> fmt::Result {
208         match self {
209             ErrorImpl::Message(msg, None) => f.write_str(msg),
210             ErrorImpl::Message(msg, Some(Pos { mark: _, path })) => {
211                 if path != "." {
212                     write!(f, "{}: ", path)?;
213                 }
214                 f.write_str(msg)
215             }
216             ErrorImpl::Libyaml(_) => unreachable!(),
217             ErrorImpl::Io(err) => Display::fmt(err, f),
218             ErrorImpl::FromUtf8(err) => Display::fmt(err, f),
219             ErrorImpl::EndOfStream => f.write_str("EOF while parsing a value"),
220             ErrorImpl::MoreThanOneDocument => f.write_str(
221                 "deserializing from YAML containing more than one document is not supported",
222             ),
223             ErrorImpl::RecursionLimitExceeded(_mark) => f.write_str("recursion limit exceeded"),
224             ErrorImpl::RepetitionLimitExceeded => f.write_str("repetition limit exceeded"),
225             ErrorImpl::BytesUnsupported => {
226                 f.write_str("serialization and deserialization of bytes in YAML is not implemented")
227             }
228             ErrorImpl::UnknownAnchor(_mark) => f.write_str("unknown anchor"),
229             ErrorImpl::SerializeNestedEnum => {
230                 f.write_str("serializing nested enums in YAML is not supported yet")
231             }
232             ErrorImpl::ScalarInMerge => {
233                 f.write_str("expected a mapping or list of mappings for merging, but found scalar")
234             }
235             ErrorImpl::TaggedInMerge => f.write_str("unexpected tagged value in merge"),
236             ErrorImpl::ScalarInMergeElement => {
237                 f.write_str("expected a mapping for merging, but found scalar")
238             }
239             ErrorImpl::SequenceInMergeElement => {
240                 f.write_str("expected a mapping for merging, but found sequence")
241             }
242             ErrorImpl::EmptyTag => f.write_str("empty YAML tag is not allowed"),
243             ErrorImpl::FailedToParseNumber => f.write_str("failed to parse YAML number"),
244             ErrorImpl::Shared(_) => unreachable!(),
245         }
246     }
247 
display(&self, f: &mut fmt::Formatter) -> fmt::Result248     fn display(&self, f: &mut fmt::Formatter) -> fmt::Result {
249         match self {
250             ErrorImpl::Libyaml(err) => Display::fmt(err, f),
251             ErrorImpl::Shared(err) => err.display(f),
252             _ => {
253                 self.message_no_mark(f)?;
254                 if let Some(mark) = self.mark() {
255                     if mark.line() != 0 || mark.column() != 0 {
256                         write!(f, " at {}", mark)?;
257                     }
258                 }
259                 Ok(())
260             }
261         }
262     }
263 
debug(&self, f: &mut fmt::Formatter) -> fmt::Result264     fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
265         match self {
266             ErrorImpl::Libyaml(err) => Debug::fmt(err, f),
267             ErrorImpl::Shared(err) => err.debug(f),
268             _ => {
269                 f.write_str("Error(")?;
270                 struct MessageNoMark<'a>(&'a ErrorImpl);
271                 impl<'a> Display for MessageNoMark<'a> {
272                     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
273                         self.0.message_no_mark(f)
274                     }
275                 }
276                 let msg = MessageNoMark(self).to_string();
277                 Debug::fmt(&msg, f)?;
278                 if let Some(mark) = self.mark() {
279                     write!(
280                         f,
281                         ", line: {}, column: {}",
282                         mark.line() + 1,
283                         mark.column() + 1,
284                     )?;
285                 }
286                 f.write_str(")")
287             }
288         }
289     }
290 }
291