use crate::{
libyml::{emitter, error as libyml},
modules::path::Path,
};
use serde::{de, ser};
use std::{
error::Error as StdError,
fmt::{self, Debug, Display},
io, result, string,
sync::Arc,
};
#[derive(Debug)]
pub struct Pos {
mark: libyml::Mark,
path: String,
}
#[derive(Clone, Copy, Debug)]
pub struct Location {
index: usize,
line: usize,
column: usize,
}
impl Location {
pub fn index(&self) -> usize {
self.index
}
pub fn line(&self) -> usize {
self.line
}
pub fn column(&self) -> usize {
self.column
}
#[doc(hidden)]
fn from_mark(mark: libyml::Mark) -> Self {
Location {
index: mark.index() as usize,
line: mark.line() as usize + 1,
column: mark.column() as usize + 1,
}
}
}
pub struct Error(Box<ErrorImpl>);
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug)]
pub enum ErrorImpl {
Message(String, Option<Pos>),
Libyml(libyml::Error),
IoError(io::Error),
FromUtf8(string::FromUtf8Error),
EndOfStream,
MoreThanOneDocument,
RecursionLimitExceeded(libyml::Mark),
RepetitionLimitExceeded,
BytesUnsupported,
UnknownAnchor(libyml::Mark),
SerializeNestedEnum,
ScalarInMerge,
TaggedInMerge,
ScalarInMergeElement,
SequenceInMergeElement,
EmptyTag,
FailedToParseNumber,
Shared(Arc<ErrorImpl>),
}
impl Display for ErrorImpl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorImpl::Message(msg, _) => write!(f, "Error: {}", msg),
ErrorImpl::Libyml(_) => write!(f, "Error: An error occurred in the Libyml library"),
ErrorImpl::IoError(err) => write!(f, "I/O Error: {}", err),
ErrorImpl::FromUtf8(err) => write!(f, "UTF-8 Conversion Error: {}", err),
ErrorImpl::EndOfStream => write!(f, "Unexpected End of YAML Stream: The YAML stream ended unexpectedly while parsing a value"),
ErrorImpl::MoreThanOneDocument => write!(f, "Multiple YAML Documents Error: Deserializing from YAML containing more than one document is not supported"),
ErrorImpl::RecursionLimitExceeded(_) => write!(f, "Recursion Limit Exceeded: The recursive depth limit was exceeded while parsing the YAML"),
ErrorImpl::RepetitionLimitExceeded => write!(f, "Repetition Limit Exceeded: The repetition limit was exceeded while parsing the YAML"),
ErrorImpl::BytesUnsupported => write!(f, "Unsupported Bytes Error: Serialization and deserialization of bytes in YAML is not implemented"),
ErrorImpl::UnknownAnchor(_) => write!(f, "Unknown Anchor Error: An unknown anchor was encountered in the YAML"),
ErrorImpl::SerializeNestedEnum => write!(f, "Nested Enum Serialization Error: Serializing nested enums in YAML is not supported"),
ErrorImpl::ScalarInMerge => write!(f, "Invalid Merge Error: Expected a mapping or list of mappings for merging, but found a scalar value"),
ErrorImpl::TaggedInMerge => write!(f, "Invalid Merge Error: Unexpected tagged value encountered in a merge operation"),
ErrorImpl::ScalarInMergeElement => write!(f, "Invalid Merge Element Error: Expected a mapping for merging, but found a scalar value"),
ErrorImpl::SequenceInMergeElement => write!(f, "Invalid Merge Element Error: Expected a mapping for merging, but found a sequence"),
ErrorImpl::EmptyTag => write!(f, "Empty Tag Error: Empty YAML tags are not allowed"),
ErrorImpl::FailedToParseNumber => write!(f, "Number Parsing Error: Failed to parse the YAML number"),
ErrorImpl::Shared(_) => write!(f, "Shared Error: An error occurred in the shared error implementation"),
}
}
}
impl Error {
pub fn io_error(&self) -> Option<&io::Error> {
if let ErrorImpl::IoError(err) = &*self.0 {
Some(err)
} else {
None
}
}
pub fn location(&self) -> Option<Location> {
self.0.location()
}
pub fn shared(self) -> Arc<ErrorImpl> {
if let ErrorImpl::Shared(err) = *self.0 {
err
} else {
Arc::from(self.0)
}
}
}
pub fn new(inner: ErrorImpl) -> Error {
Error(Box::new(inner))
}
pub fn shared(shared: Arc<ErrorImpl>) -> Error {
Error(Box::new(ErrorImpl::Shared(shared)))
}
pub fn fix_mark(
mut error: Error,
mark: libyml::Mark,
path: Path<'_>,
) -> Error {
if let ErrorImpl::Message(_, none @ None) = error.0.as_mut() {
*none = Some(Pos {
mark,
path: path.to_string(),
});
}
error
}
impl From<libyml::Error> for Error {
fn from(err: libyml::Error) -> Self {
Error(Box::new(ErrorImpl::Libyml(err)))
}
}
impl From<emitter::Error> for Error {
fn from(err: emitter::Error) -> Self {
match err {
emitter::Error::Libyml(err) => Self::from(err),
emitter::Error::Io(err) => new(ErrorImpl::IoError(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)
}
}
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::IoError(err) => err.source(),
ErrorImpl::FromUtf8(err) => err.source(),
ErrorImpl::Shared(err) => err.source(),
_ => None,
}
}
fn mark(&self) -> Option<libyml::Mark> {
match self {
ErrorImpl::Message(_, Some(Pos { mark, path: _ }))
| ErrorImpl::RecursionLimitExceeded(mark)
| ErrorImpl::UnknownAnchor(mark) => Some(*mark),
ErrorImpl::Libyml(err) => Some(err.mark()),
ErrorImpl::Shared(err) => err.mark(),
_ => None,
}
}
fn message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorImpl::Message(description, None) => f.write_str(description),
ErrorImpl::Message(description, Some(Pos { mark: _, path })) => {
if path != "." {
write!(f, "{}: ", path)?;
}
f.write_str(description)
}
ErrorImpl::Libyml(_) => unreachable!(),
ErrorImpl::IoError(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::Libyml(err) => Display::fmt(err, f),
ErrorImpl::Shared(err) => err.display(f),
_ => {
self.message(f)?;
if let Some(location) = self.mark() {
if location.line() != 0 || location.column() != 0 {
write!(f, " at {}", location)?;
}
}
Ok(())
}
}
}
fn debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorImpl::Libyml(err) => Debug::fmt(err, f),
ErrorImpl::Shared(err) => err.debug(f),
_ => {
f.write_str("Error(")?;
struct MessageNoMark<'a>(&'a ErrorImpl);
impl Display for MessageNoMark<'_> {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
self.0.message(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(")")
}
}
}
}