use alloc::borrow::Cow;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
use core::any::{Any, TypeId};
use core::fmt;
use crate::{Position, State};
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum ErrorKind {
UnsupportedType,
Unexpected,
MissingField,
OutOfRange,
WrongLength,
EndOfFile,
Io,
}
pub trait ErrorAttachment: Any + fmt::Debug + Send + Sync {
fn fmt_context(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let _ = f;
Ok(())
}
}
pub trait ErrorContext: 'static {
fn add_context(err: Error, state: &State) -> Error;
}
pub struct Error {
inner: Box<ErrorInner>,
}
enum ErrorInner {
Single(ErrorData),
Multiple(Vec<Error>),
}
#[derive(Debug)]
struct ErrorData {
kind: ErrorKind,
msg: Cow<'static, str>,
source: Option<Box<dyn core::error::Error + Send + Sync>>,
offset: Option<usize>,
line_column: Option<(usize, usize)>,
attachments: Vec<Attachment>,
has_context: bool,
collected: bool,
}
#[derive(Debug)]
struct Attachment {
type_id: TypeId,
value: Box<dyn ErrorAttachment>,
}
impl Error {
#[cold]
pub fn new<M: Into<Cow<'static, str>>>(kind: ErrorKind, msg: M) -> Error {
Error {
inner: Box::new(ErrorInner::Single(ErrorData {
kind,
msg: msg.into(),
source: None,
offset: None,
line_column: None,
attachments: Vec::new(),
has_context: false,
collected: false,
})),
}
}
pub fn from_errors<I: IntoIterator<Item = Error>>(errors: I) -> Option<Error> {
let mut errors = errors.into_iter();
let mut rv = errors.next()?;
for err in errors {
rv.push_error(err);
}
Some(rv)
}
#[cold]
pub fn in_progress() -> Error {
Error::new(
ErrorKind::Unexpected,
"a value was only partially written, the stream cannot continue",
)
}
pub fn push_error(&mut self, err: Error) {
let errors = self.make_multiple();
match *err.inner {
ErrorInner::Single(data) => errors.push(Error {
inner: Box::new(ErrorInner::Single(data)),
}),
ErrorInner::Multiple(others) => errors.extend(others),
}
}
fn make_multiple(&mut self) -> &mut Vec<Error> {
if let ErrorInner::Single(_) = *self.inner {
let first = core::mem::replace(&mut *self.inner, ErrorInner::Multiple(Vec::new()));
if let ErrorInner::Multiple(ref mut errors) = *self.inner {
errors.push(Error {
inner: Box::new(first),
});
}
}
match *self.inner {
ErrorInner::Multiple(ref mut errors) => errors,
ErrorInner::Single(_) => unreachable!(),
}
}
pub fn errors(&self) -> impl Iterator<Item = &Error> {
match *self.inner {
ErrorInner::Single(_) => core::slice::from_ref(self).iter(),
ErrorInner::Multiple(ref errors) => errors.iter(),
}
}
pub fn error_count(&self) -> usize {
match *self.inner {
ErrorInner::Single(_) => 1,
ErrorInner::Multiple(ref errors) => errors.len(),
}
}
fn data(&self) -> &ErrorData {
match *self.inner {
ErrorInner::Single(ref data) => data,
ErrorInner::Multiple(ref errors) => errors[0].data(),
}
}
fn data_mut(&mut self) -> &mut ErrorData {
match *self.inner {
ErrorInner::Single(ref mut data) => data,
ErrorInner::Multiple(ref mut errors) => errors[0].data_mut(),
}
}
pub(crate) fn map_each(mut self, mut f: impl FnMut(Error) -> Error) -> Error {
if let ErrorInner::Multiple(ref mut errors) = *self.inner {
for err in errors.iter_mut() {
let taken = core::mem::replace(err, Error::new(ErrorKind::Unexpected, ""));
*err = f(taken);
}
self
} else {
f(self)
}
}
pub(crate) fn uncollected_count(&self) -> usize {
self.errors().filter(|err| !err.data().collected).count()
}
pub(crate) fn mark_collected(mut self) -> Error {
self = self.map_each(|mut err| {
err.data_mut().collected = true;
err
});
self
}
pub fn with_source<E: core::error::Error + Send + Sync + 'static>(mut self, source: E) -> Self {
self.data_mut().source = Some(Box::new(source));
self
}
pub fn kind(&self) -> ErrorKind {
self.data().kind
}
pub fn message(&self) -> &str {
&self.data().msg
}
pub fn with_offset(mut self, offset: usize) -> Self {
let data = self.data_mut();
data.offset = Some(offset);
data.line_column = None;
self
}
pub fn with_position(mut self, offset: usize, line: usize, column: usize) -> Self {
let data = self.data_mut();
data.offset = Some(offset);
data.line_column = Some((line, column));
self
}
pub fn resolve_position(self, source: &[u8]) -> Self {
self.map_each(|mut err| {
let data = err.data_mut();
if let (Some(offset), None) = (data.offset, data.line_column) {
let pos = Position::of(source, offset);
data.line_column = Some((pos.line, pos.column));
}
err
})
}
pub(crate) fn shift_position(self, base: Position) -> Self {
self.map_each(|mut err| {
let data = err.data_mut();
if let Some(ref mut error_offset) = data.offset {
*error_offset += base.offset;
}
if let Some((ref mut error_line, ref mut error_column)) = data.line_column {
if *error_line == 1 {
*error_column += base.column - 1;
}
*error_line += base.line - 1;
}
err
})
}
pub fn offset(&self) -> Option<usize> {
self.data().offset
}
pub fn line(&self) -> Option<usize> {
self.data().line_column.map(|x| x.0)
}
pub fn column(&self) -> Option<usize> {
self.data().line_column.map(|x| x.1)
}
pub fn with_attachment<T: ErrorAttachment>(mut self, value: T) -> Self {
let type_id = TypeId::of::<T>();
let value = Box::new(value);
let attachments = &mut self.data_mut().attachments;
match attachments.iter_mut().find(|x| x.type_id == type_id) {
Some(attachment) => attachment.value = value,
None => attachments.push(Attachment { type_id, value }),
}
self
}
pub fn attachment<T: ErrorAttachment>(&self) -> Option<&T> {
let type_id = TypeId::of::<T>();
let attachment = self
.data()
.attachments
.iter()
.find(|x| x.type_id == type_id)?;
(&*attachment.value as &dyn Any).downcast_ref()
}
pub fn attachment_mut<T: ErrorAttachment>(&mut self) -> Option<&mut T> {
let type_id = TypeId::of::<T>();
let attachment = self
.data_mut()
.attachments
.iter_mut()
.find(|x| x.type_id == type_id)?;
(&mut *attachment.value as &mut dyn Any).downcast_mut()
}
pub fn attachments(&self) -> impl Iterator<Item = &dyn ErrorAttachment> {
self.data().attachments.iter().map(|x| &*x.value)
}
pub(crate) fn has_context(&self) -> bool {
self.data().has_context
}
pub(crate) fn set_has_context(&mut self) {
self.data_mut().has_context = true;
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let data = match *self.inner {
ErrorInner::Single(ref data) => data,
ErrorInner::Multiple(ref errors) => {
return f.debug_tuple("Errors").field(errors).finish();
}
};
let mut s = f.debug_struct("Error");
s.field("kind", &data.kind).field("msg", &data.msg);
if let Some(offset) = data.offset {
s.field("offset", &offset);
}
if let Some((line, column)) = data.line_column {
s.field("line", &line).field("column", &column);
}
if !data.attachments.is_empty() {
s.field("attachments", &DebugAttachments(&data.attachments));
}
s.field("source", &data.source).finish()
}
}
impl ErrorData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}: {}", self.kind, self.msg)?;
match (self.line_column, self.offset) {
(Some((line, column)), _) => write!(f, " at line {} column {}", line, column)?,
(None, Some(offset)) => write!(f, " at offset {}", offset)?,
(None, None) => {}
}
for attachment in self.attachments.iter() {
attachment.value.fmt_context(f)?;
}
Ok(())
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let errors = match *self.inner {
ErrorInner::Single(ref data) => return data.fmt(f),
ErrorInner::Multiple(ref errors) => errors,
};
errors[0].data().fmt(f)?;
if f.alternate() {
for err in &errors[1..] {
writeln!(f)?;
err.data().fmt(f)?;
}
} else if errors.len() == 2 {
write!(f, " (and 1 more error)")?;
} else {
write!(f, " (and {} more errors)", errors.len() - 1)?;
}
Ok(())
}
}
struct DebugAttachments<'a>(&'a [Attachment]);
impl fmt::Debug for DebugAttachments<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list()
.entries(self.0.iter().map(|x| &x.value))
.finish()
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error {
Error::new(ErrorKind::Io, err.to_string()).with_source(err)
}
}
impl core::error::Error for Error {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
self.data().source.as_ref().map(|err| err.as_ref() as _)
}
}
#[cold]
#[inline(never)]
pub(crate) fn discarded_error(kind: ErrorKind) -> Error {
Error::new(kind, "discarded error")
}
#[cold]
pub(crate) fn conversion_error<E: fmt::Display>(err: E) -> Error {
Error::new(ErrorKind::Unexpected, format!("invalid value: {}", err))
}
#[cold]
pub fn unknown_variant(tag: Option<&str>, type_name: &str, names: &[&str]) -> Error {
let mut msg = String::from("unknown variant");
if let Some(tag) = tag {
msg.push_str(" `");
msg.push_str(tag);
msg.push('`');
}
msg.push_str(" of ");
msg.push_str(type_name);
push_expected(&mut msg, names, "variants");
Error::new(ErrorKind::Unexpected, msg)
}
pub(crate) fn push_expected(msg: &mut String, names: &[&str], what: &str) {
match names {
[] => {
msg.push_str(", there are no ");
msg.push_str(what);
}
[name] => {
msg.push_str(", expected `");
msg.push_str(name);
msg.push('`');
}
[first, second] => {
msg.push_str(", expected `");
msg.push_str(first);
msg.push_str("` or `");
msg.push_str(second);
msg.push('`');
}
names => {
msg.push_str(", expected one of ");
for (idx, name) in names.iter().enumerate() {
if idx > 0 {
msg.push_str(", ");
}
msg.push('`');
msg.push_str(name);
msg.push('`');
}
}
}
}