use std::fmt::{Debug, Display};
pub type Result<T> = ::std::result::Result<T, Error>;
#[derive(Debug)]
pub struct Error {
inner: anyhow::Error,
recoverable: bool,
}
impl Error {
pub fn new<E>(err: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self {
inner: anyhow::Error::new(err),
recoverable: false,
}
}
pub fn message<D>(message: D) -> Self
where
D: Display + Debug + Send + Sync + 'static,
{
Self {
inner: anyhow::Error::msg(message),
recoverable: false,
}
}
pub fn new_recoverable<E>(err: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self {
inner: anyhow::Error::new(err),
recoverable: true,
}
}
pub fn message_recoverable<D>(message: D) -> Self
where
D: Display + Debug + Send + Sync + 'static,
{
Self {
inner: anyhow::Error::msg(message),
recoverable: true,
}
}
pub fn context<D>(self, message: D) -> Self
where
D: Display + Send + Sync + 'static,
{
Self {
inner: self.inner.context(message),
recoverable: self.recoverable,
}
}
pub fn is_recoverable(&self) -> bool {
self.recoverable
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Load Error: {:?}", self.inner)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(self.inner.as_ref())
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum Kind {
VersionMismatch,
MissingField,
TypeMismatch,
UnknownVersion,
UnknownVariant,
NumberOutOfRange,
MissingFile,
}
impl Kind {
pub const fn as_str(self) -> &'static str {
match self {
Self::VersionMismatch => "version mismatch",
Self::MissingField => "missing field",
Self::TypeMismatch => "type mismatch",
Self::UnknownVersion => "unknown version",
Self::UnknownVariant => "unknown variant",
Self::NumberOutOfRange => "number out of range for target type",
Self::MissingFile => "handle references a file not present in the manifest",
}
}
pub const fn is_recoverable(self) -> bool {
match self {
Self::VersionMismatch | Self::MissingField | Self::TypeMismatch => true,
Self::UnknownVersion
| Self::UnknownVariant
| Self::NumberOutOfRange
| Self::MissingFile => false,
}
}
}
impl std::error::Error for Kind {}
impl std::fmt::Display for Kind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl From<Kind> for Error {
fn from(kind: Kind) -> Self {
Self {
inner: anyhow::Error::new(kind),
recoverable: kind.is_recoverable(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn context_preserves_recoverable_flag() {
assert!(
Error::from(Kind::TypeMismatch)
.context("extra")
.is_recoverable()
);
assert!(
!Error::from(Kind::MissingFile)
.context("extra")
.is_recoverable()
);
}
}