| use crate::libyaml::{emitter, error as libyaml}; |
| use crate::path::Path; |
| use serde::{de, ser}; |
| use std::error::Error as StdError; |
| use std::fmt::{self, Debug, Display}; |
| use std::io; |
| use std::result; |
| use std::string; |
| use std::sync::Arc; |
| |
| /// An error that happened serializing or deserializing YAML data. |
| pub struct Error(Box<ErrorImpl>); |
| |
| /// Alias for a `Result` with the error type `serde_yaml::Error`. |
| pub type Result<T> = result::Result<T, Error>; |
| |
| #[derive(Debug)] |
| pub(crate) enum ErrorImpl { |
| Message(String, Option<Pos>), |
| |
| Libyaml(libyaml::Error), |
| Io(io::Error), |
| FromUtf8(string::FromUtf8Error), |
| |
| EndOfStream, |
| MoreThanOneDocument, |
| RecursionLimitExceeded(libyaml::Mark), |
| RepetitionLimitExceeded, |
| BytesUnsupported, |
| UnknownAnchor(libyaml::Mark), |
| SerializeNestedEnum, |
| ScalarInMerge, |
| TaggedInMerge, |
| ScalarInMergeElement, |
| SequenceInMergeElement, |
| EmptyTag, |
| FailedToParseNumber, |
| |
| Shared(Arc<ErrorImpl>), |
| } |
| |
| #[derive(Debug)] |
| pub(crate) struct Pos { |
| mark: libyaml::Mark, |
| path: String, |
| } |
| |
| /// The input location that an error occured. |
| #[derive(Debug)] |
| pub struct Location { |
| index: usize, |
| line: usize, |
| column: usize, |
| } |
| |
| impl Location { |
| /// The byte index of the error |
| pub fn index(&self) -> usize { |
| self.index |
| } |
| |
| /// The line of the error |
| pub fn line(&self) -> usize { |
| self.line |
| } |
| |
| /// The column of the error |
| pub fn column(&self) -> usize { |
| self.column |
| } |
| |
| // This is to keep decoupled with the yaml crate |
| #[doc(hidden)] |
| fn from_mark(mark: libyaml::Mark) -> Self { |
| Location { |
| index: mark.index() as usize, |
| // `line` and `column` returned from libyaml are 0-indexed but all error messages add +1 to this value |
| line: mark.line() as usize + 1, |
| column: mark.column() as usize + 1, |
| } |
| } |
| } |
| |
| impl Error { |
| /// Returns the Location from the error if one exists. |
| /// |
| /// Not all types of errors have a location so this can return `None`. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # use serde_yaml::{Value, Error}; |
| /// # |
| /// // The `@` character as the first character makes this invalid yaml |
| /// let invalid_yaml: Result<Value, Error> = serde_yaml::from_str("@invalid_yaml"); |
| /// |
| /// let location = invalid_yaml.unwrap_err().location().unwrap(); |
| /// |
| /// assert_eq!(location.line(), 1); |
| /// assert_eq!(location.column(), 1); |
| /// ``` |
| pub fn location(&self) -> Option<Location> { |
| self.0.location() |
| } |
| } |
| |
| pub(crate) fn new(inner: ErrorImpl) -> Error { |
| Error(Box::new(inner)) |
| } |
| |
| pub(crate) fn shared(shared: Arc<ErrorImpl>) -> Error { |
| Error(Box::new(ErrorImpl::Shared(shared))) |
| } |
| |
| pub(crate) fn fix_mark(mut error: Error, mark: libyaml::Mark, path: Path) -> Error { |
| if let ErrorImpl::Message(_, none @ None) = error.0.as_mut() { |
| *none = Some(Pos { |
| mark, |
| path: path.to_string(), |
| }); |
| } |
| error |
| } |
| |
| impl Error { |
| pub(crate) fn shared(self) -> Arc<ErrorImpl> { |
| if let ErrorImpl::Shared(err) = *self.0 { |
| err |
| } else { |
| Arc::from(self.0) |
| } |
| } |
| } |
| |
| impl From<libyaml::Error> for Error { |
| fn from(err: libyaml::Error) -> Self { |
| Error(Box::new(ErrorImpl::Libyaml(err))) |
| } |
| } |
| |
| impl From<emitter::Error> for Error { |
| fn from(err: emitter::Error) -> Self { |
| match err { |
| emitter::Error::Libyaml(err) => Self::from(err), |
| emitter::Error::Io(err) => new(ErrorImpl::Io(err)), |
| } |
| } |
| } |
| |
| impl StdError for Error { |
| fn source(&self) -> Option<&(dyn StdError + 'static)> { |
| self.0.source() |
| } |
| } |
| |
| impl Display for Error { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.0.display(f) |
| } |
| } |
| |
| // Remove two layers of verbosity from the debug representation. Humans often |
| // end up seeing this representation because it is what unwrap() shows. |
| impl Debug for Error { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.0.debug(f) |
| } |
| } |
| |
| impl ser::Error for Error { |
| fn custom<T: Display>(msg: T) -> Self { |
| Error(Box::new(ErrorImpl::Message(msg.to_string(), None))) |
| } |
| } |
| |
| impl de::Error for Error { |
| fn custom<T: Display>(msg: T) -> Self { |
| Error(Box::new(ErrorImpl::Message(msg.to_string(), None))) |
| } |
| } |
| |
| impl ErrorImpl { |
| fn location(&self) -> Option<Location> { |
| self.mark().map(Location::from_mark) |
| } |
| |
| fn source(&self) -> Option<&(dyn StdError + 'static)> { |
| match self { |
| ErrorImpl::Io(err) => err.source(), |
| ErrorImpl::FromUtf8(err) => err.source(), |
| ErrorImpl::Shared(err) => err.source(), |
| _ => None, |
| } |
| } |
| |
| fn mark(&self) -> Option<libyaml::Mark> { |
| match self { |
| ErrorImpl::Message(_, Some(Pos { mark, path: _ })) |
| | ErrorImpl::RecursionLimitExceeded(mark) |
| | ErrorImpl::UnknownAnchor(mark) => Some(*mark), |
| ErrorImpl::Libyaml(err) => Some(err.mark()), |
| ErrorImpl::Shared(err) => err.mark(), |
| _ => None, |
| } |
| } |
| |
| fn message_no_mark(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match self { |
| ErrorImpl::Message(msg, None) => f.write_str(msg), |
| ErrorImpl::Message(msg, Some(Pos { mark: _, path })) => { |
| if path != "." { |
| write!(f, "{}: ", path)?; |
| } |
| f.write_str(msg) |
| } |
| ErrorImpl::Libyaml(_) => unreachable!(), |
| ErrorImpl::Io(err) => Display::fmt(err, f), |
| ErrorImpl::FromUtf8(err) => Display::fmt(err, f), |
| ErrorImpl::EndOfStream => f.write_str("EOF while parsing a value"), |
| ErrorImpl::MoreThanOneDocument => f.write_str( |
| "deserializing from YAML containing more than one document is not supported", |
| ), |
| ErrorImpl::RecursionLimitExceeded(_mark) => f.write_str("recursion limit exceeded"), |
| ErrorImpl::RepetitionLimitExceeded => f.write_str("repetition limit exceeded"), |
| ErrorImpl::BytesUnsupported => { |
| f.write_str("serialization and deserialization of bytes in YAML is not implemented") |
| } |
| ErrorImpl::UnknownAnchor(_mark) => f.write_str("unknown anchor"), |
| ErrorImpl::SerializeNestedEnum => { |
| f.write_str("serializing nested enums in YAML is not supported yet") |
| } |
| ErrorImpl::ScalarInMerge => { |
| f.write_str("expected a mapping or list of mappings for merging, but found scalar") |
| } |
| ErrorImpl::TaggedInMerge => f.write_str("unexpected tagged value in merge"), |
| ErrorImpl::ScalarInMergeElement => { |
| f.write_str("expected a mapping for merging, but found scalar") |
| } |
| ErrorImpl::SequenceInMergeElement => { |
| f.write_str("expected a mapping for merging, but found sequence") |
| } |
| ErrorImpl::EmptyTag => f.write_str("empty YAML tag is not allowed"), |
| ErrorImpl::FailedToParseNumber => f.write_str("failed to parse YAML number"), |
| ErrorImpl::Shared(_) => unreachable!(), |
| } |
| } |
| |
| fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match self { |
| ErrorImpl::Libyaml(err) => Display::fmt(err, f), |
| ErrorImpl::Shared(err) => err.display(f), |
| _ => { |
| self.message_no_mark(f)?; |
| if let Some(mark) = self.mark() { |
| if mark.line() != 0 || mark.column() != 0 { |
| write!(f, " at {}", mark)?; |
| } |
| } |
| Ok(()) |
| } |
| } |
| } |
| |
| fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match self { |
| ErrorImpl::Libyaml(err) => Debug::fmt(err, f), |
| ErrorImpl::Shared(err) => err.debug(f), |
| _ => { |
| f.write_str("Error(")?; |
| struct MessageNoMark<'a>(&'a ErrorImpl); |
| impl<'a> Display for MessageNoMark<'a> { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.0.message_no_mark(f) |
| } |
| } |
| let msg = MessageNoMark(self).to_string(); |
| Debug::fmt(&msg, f)?; |
| if let Some(mark) = self.mark() { |
| write!( |
| f, |
| ", line: {}, column: {}", |
| mark.line() + 1, |
| mark.column() + 1, |
| )?; |
| } |
| f.write_str(")") |
| } |
| } |
| } |
| } |