serde_yaml/
error.rs

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