Skip to main content

serde_yaml/
error.rs

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