serde_yaml/
error.rs

1use crate::libyaml::{emitter, error as libyaml};
2use crate::path::Path;
3use serde::{de, ser};
4use std::error::Error as StdError;
5use std::fmt::{self, Debug, Display};
6use std::io;
7use std::result;
8use std::string;
9use std::sync::Arc;
10
11/// An error that happened serializing or deserializing YAML data.
12pub struct Error(Box<ErrorImpl>);
13
14/// Alias for a `Result` with the error type `serde_yaml::Error`.
15pub type Result<T> = result::Result<T, Error>;
16
17#[derive(Debug)]
18pub(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)]
43pub(crate) struct Pos {
44    mark: libyaml::Mark,
45    path: String,
46}
47
48/// The input location that an error occured.
49#[derive(Debug)]
50pub struct Location {
51    index: usize,
52    line: usize,
53    column: usize,
54}
55
56impl Location {
57    /// The byte index of the error
58    pub fn index(&self) -> usize {
59        self.index
60    }
61
62    /// The line of the error
63    pub fn line(&self) -> usize {
64        self.line
65    }
66
67    /// The column of the error
68    pub fn column(&self) -> usize {
69        self.column
70    }
71
72    // This is to keep decoupled with the yaml crate
73    #[doc(hidden)]
74    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
84impl 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    /// ```
102    pub fn location(&self) -> Option<Location> {
103        self.0.location()
104    }
105}
106
107pub(crate) fn new(inner: ErrorImpl) -> Error {
108    Error(Box::new(inner))
109}
110
111pub(crate) fn shared(shared: Arc<ErrorImpl>) -> Error {
112    Error(Box::new(ErrorImpl::Shared(shared)))
113}
114
115pub(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
125impl Error {
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
135impl From<libyaml::Error> for Error {
136    fn from(err: libyaml::Error) -> Self {
137        Error(Box::new(ErrorImpl::Libyaml(err)))
138    }
139}
140
141impl From<emitter::Error> for Error {
142    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
150impl StdError for Error {
151    fn source(&self) -> Option<&(dyn StdError + 'static)> {
152        self.0.source()
153    }
154}
155
156impl Display for Error {
157    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.
164impl Debug for Error {
165    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
166        self.0.debug(f)
167    }
168}
169
170impl ser::Error for Error {
171    fn custom<T: Display>(msg: T) -> Self {
172        Error(Box::new(ErrorImpl::Message(msg.to_string(), None)))
173    }
174}
175
176impl de::Error for Error {
177    fn custom<T: Display>(msg: T) -> Self {
178        Error(Box::new(ErrorImpl::Message(msg.to_string(), None)))
179    }
180}
181
182impl ErrorImpl {
183    fn location(&self) -> Option<Location> {
184        self.mark().map(Location::from_mark)
185    }
186
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
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
207    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
248    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
264    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}