use core::fmt::{Display, Formatter};
use core::num::TryFromIntError;
use core::ops::Range;
use core::write;
#[cfg(feature = "alloc")]
use core::marker::{Send, Sync};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::collections::TryReserveError;
#[cfg(feature = "alloc")]
use alloc::string::FromUtf8Error;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
#[cfg(feature = "alloc")]
source: Option<Box<dyn core::error::Error + Send + Sync + 'static>>,
}
impl Error {
pub fn new(kind: ErrorKind) -> Self {
Self {
kind,
#[cfg(feature = "alloc")]
source: None,
}
}
#[cfg(feature = "alloc")]
pub fn new_with_source(kind: ErrorKind, source: impl core::error::Error + Send + Sync + 'static) -> Self {
Self {
kind,
source: Some(Box::new(source)),
}
}
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
self.kind.fmt(f)?;
#[cfg(feature = "alloc")]
if let Some(source) = &self.source {
f.write_str("\nError was caused by:\n")?;
source.fmt(f)?;
}
Ok(())
}
}
#[cfg(feature = "alloc")]
impl core::error::Error for Error {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
self.source.as_deref().map(|e| e as &(dyn core::error::Error + 'static))
}
}
impl<T: Into<ErrorKind>> From<T> for Error {
fn from(value: T) -> Self {
Self::new(value.into())
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum ErrorKind {
UnexpectedEob {
requested: usize,
remaining: usize,
},
InvalidReservation {
buffer_len: usize,
reserved_range: Range<usize>,
},
#[cfg(feature = "alloc")]
AllocationError(TryReserveError),
AllocationLimitReached {
requested: usize,
remaining: usize,
},
InvalidData(InvalidDataErrorKind),
}
impl Display for ErrorKind {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::UnexpectedEob { requested, remaining } => {
write!(f, "unexpected end of buffer: attempted to read '{requested}' bytes from a buffer with only '{remaining}' bytes remaining")
}
Self::InvalidReservation {
buffer_len,
reserved_range,
} => {
let Range { start, end } = reserved_range;
write!(
f,
"invalid reservation: range '[{start}..{end})' does not fit within buffer of length '{buffer_len}'"
)
}
Self::InvalidData(inner) => inner.fmt(f),
_ => todo!(),
}
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum InvalidDataErrorKind {
IllegalValue { desc: &'static str, value: Option<i128> },
#[cfg(feature = "alloc")]
InvalidString(FromUtf8Error),
OutOfRange {
value: i128,
min: i128,
max: i128,
typename: &'static str,
},
#[cfg(feature = "alloc")]
DuplicateDictionaryKey,
}
impl Display for InvalidDataErrorKind {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::IllegalValue { desc, value } => {
if let Some(value) = value {
write!(f, "illegal value: {desc} (value: {value})")
} else {
write!(f, "illegal value: {desc}")
}
}
#[cfg(feature = "alloc")]
Self::InvalidString(inner) => inner.fmt(f),
Self::OutOfRange { value, min, max, typename } => {
write!(
f,
"value '{value}' is outside the allowed range for type '{typename}'; values must be within [{min}..{max}]"
)
}
#[cfg(feature = "alloc")]
Self::DuplicateDictionaryKey => write!(f, "duplicate dictionary key encountered"),
}
}
}
impl From<InvalidDataErrorKind> for ErrorKind {
fn from(value: InvalidDataErrorKind) -> Self {
ErrorKind::InvalidData(value)
}
}
impl From<TryFromIntError> for Error {
fn from(_: TryFromIntError) -> Self {
Error::from(InvalidDataErrorKind::IllegalValue {
desc: "failed to convert integer",
value: None,
})
}
}
#[cfg(feature = "alloc")]
impl From<TryReserveError> for Error {
fn from(value: TryReserveError) -> Self {
Error::from(ErrorKind::AllocationError(value))
}
}
#[cfg(feature = "alloc")]
impl From<FromUtf8Error> for Error {
fn from(value: FromUtf8Error) -> Self {
Error::from(InvalidDataErrorKind::InvalidString(value))
}
}
#[cfg(feature = "std")]
impl From<Error> for std::io::Error {
fn from(value: Error) -> Self {
std::io::Error::other(value)
}
}