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 core::mem::ManuallyDrop;
use core::ops::{Deref, DerefMut};
use crate::{Position, State};
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[non_exhaustive]
pub enum ErrorKind {
Syntax,
EndOfFile,
LimitExceeded,
InvalidType,
InvalidValue,
OutOfRange,
WrongLength,
MissingField,
UnknownField,
UnknownVariant,
DuplicateKey,
UnsupportedType,
InvalidState,
Configuration,
Io,
Custom,
}
impl ErrorKind {
#[inline]
pub(crate) fn is_rejection(self) -> bool {
matches!(
self,
ErrorKind::InvalidType
| ErrorKind::InvalidValue
| ErrorKind::UnknownField
| ErrorKind::UnknownVariant
| ErrorKind::DuplicateKey
| ErrorKind::Custom
)
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[non_exhaustive]
pub enum ErrorCategory {
Syntax,
Eof,
Data,
Limit,
Unsupported,
Usage,
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: &mut Error, state: &State);
}
static RAW_REQUEST: &str = "raw value requested outside of a deserialization";
pub struct Error {
inner: ErrorBox,
}
struct ErrorBox(ManuallyDrop<Box<ErrorInner>>);
impl ErrorBox {
#[inline(always)]
fn new(inner: ErrorInner) -> ErrorBox {
ErrorBox(ManuallyDrop::new(Box::new(inner)))
}
#[inline(always)]
fn into_inner(self) -> ErrorInner {
let mut this = ManuallyDrop::new(self);
*unsafe { ManuallyDrop::take(&mut this.0) }
}
}
impl Drop for ErrorBox {
#[inline(never)]
fn drop(&mut self) {
unsafe { ManuallyDrop::drop(&mut self.0) }
}
}
impl Deref for ErrorBox {
type Target = ErrorInner;
#[inline(always)]
fn deref(&self) -> &ErrorInner {
&self.0
}
}
impl DerefMut for ErrorBox {
#[inline(always)]
fn deref_mut(&mut self) -> &mut ErrorInner {
&mut self.0
}
}
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: ErrorBox::new(ErrorInner::Single(ErrorData {
kind,
msg: msg.into(),
source: None,
offset: None,
line_column: None,
attachments: Vec::new(),
has_context: false,
collected: false,
})),
}
}
#[cold]
pub fn with_offset<M: Into<Cow<'static, str>>>(
kind: ErrorKind,
msg: M,
offset: usize,
) -> Error {
let mut err = Error::new(kind, msg);
err.set_offset(offset);
err
}
#[cold]
pub fn with_position<M: Into<Cow<'static, str>>>(
kind: ErrorKind,
msg: M,
offset: usize,
line: usize,
column: usize,
) -> Error {
let mut err = Error::new(kind, msg);
err.set_position(offset, line, column);
err
}
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::InvalidState,
"a value was only partially written, the stream cannot continue",
)
}
pub(crate) fn push_error(&mut self, err: Error) {
let errors = self.make_multiple();
if matches!(*err.inner, ErrorInner::Single(_)) {
errors.push(err);
} else if let ErrorInner::Multiple(others) = err.inner.into_inner() {
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: ErrorBox::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(),
}
}
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 for_each_mut(&mut self, mut f: impl FnMut(&mut Error)) {
if let ErrorInner::Multiple(ref mut errors) = *self.inner {
errors.iter_mut().for_each(f);
} else {
f(self)
}
}
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::Custom, ""));
*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 set_source<E: core::error::Error + Send + Sync + 'static>(&mut self, source: E) {
self.data_mut().source = Some(Box::new(source));
}
#[cold]
pub(crate) fn raw_request() -> Error {
Error::new(ErrorKind::InvalidState, RAW_REQUEST)
}
pub fn is_raw_request(&self) -> bool {
match *self.inner {
ErrorInner::Single(ErrorData {
msg: Cow::Borrowed(msg),
..
}) => core::ptr::eq(msg, RAW_REQUEST),
_ => false,
}
}
pub fn kind(&self) -> ErrorKind {
self.data().kind
}
pub fn category(&self) -> ErrorCategory {
let data = self.data();
match data.kind {
ErrorKind::Syntax => ErrorCategory::Syntax,
ErrorKind::EndOfFile => ErrorCategory::Eof,
ErrorKind::LimitExceeded => ErrorCategory::Limit,
ErrorKind::InvalidType
| ErrorKind::InvalidValue
| ErrorKind::OutOfRange
| ErrorKind::WrongLength
| ErrorKind::MissingField
| ErrorKind::UnknownField
| ErrorKind::UnknownVariant
| ErrorKind::DuplicateKey => ErrorCategory::Data,
ErrorKind::UnsupportedType => ErrorCategory::Unsupported,
ErrorKind::InvalidState | ErrorKind::Configuration => ErrorCategory::Usage,
ErrorKind::Io => ErrorCategory::Io,
ErrorKind::Custom => {
if data.has_context {
ErrorCategory::Data
} else {
ErrorCategory::Syntax
}
}
}
}
pub fn message(&self) -> &str {
&self.data().msg
}
pub fn set_offset(&mut self, offset: usize) {
let data = self.data_mut();
data.offset = Some(offset);
data.line_column = None;
}
pub fn set_position(&mut self, offset: usize, line: usize, column: usize) {
let data = self.data_mut();
data.offset = Some(offset);
data.line_column = Some((line, column));
}
pub fn resolve_position(&mut self, source: &[u8]) {
self.for_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));
}
});
}
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 set_attachment<T: ErrorAttachment>(&mut self, value: T) {
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 }),
}
}
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 {
let mut rv = Error::new(ErrorKind::Io, err.to_string());
rv.set_source(err);
rv
}
}
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::InvalidValue, 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::UnknownVariant, 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('`');
}
}
}
}