use std::convert::From;
use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::result;
use minijinja::Error as JinjaError;
use nix::Error as NixError;
use serde_norway::Error as YamlError;
pub type Result<T> = result::Result<T, Error>;
pub struct Error {
repr: Repr,
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.repr, f)
}
}
enum Repr {
Simple(ErrorKind),
Custom(Box<Custom>),
}
#[derive(Debug)]
struct Custom {
kind: ErrorKind,
error: Box<dyn StdError + Send + Sync>,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum ErrorKind {
GracefulExit,
NotFound,
InvalidData,
IOError,
OmitParam,
SubprocessFail,
EmptyTaskStack,
JinjaRenderError,
Other,
}
impl ErrorKind {
pub fn as_str(self) -> &'static str {
match self {
ErrorKind::GracefulExit => "program finish gracefully",
ErrorKind::NotFound => "entity not found",
ErrorKind::InvalidData => "invalid data",
ErrorKind::IOError => "I/O error",
ErrorKind::OmitParam => "omit param",
ErrorKind::SubprocessFail => "subprocess fail",
ErrorKind::EmptyTaskStack => "task stack is empty",
ErrorKind::JinjaRenderError => "Jinja2 failed to render template",
ErrorKind::Other => "other os error",
}
}
}
impl From<ErrorKind> for Error {
#[inline]
fn from(kind: ErrorKind) -> Error {
Error {
repr: Repr::Simple(kind),
}
}
}
impl From<io::Error> for Error {
#[inline]
fn from(e: io::Error) -> Error {
Error::new(ErrorKind::IOError, e)
}
}
impl From<YamlError> for Error {
#[inline]
fn from(error: YamlError) -> Error {
Error {
repr: Repr::Custom(Box::new(Custom {
kind: ErrorKind::InvalidData,
error: Box::new(error),
})),
}
}
}
impl From<NixError> for Error {
#[inline]
fn from(error: NixError) -> Error {
Error {
repr: Repr::Custom(Box::new(Custom {
kind: ErrorKind::Other,
error: Box::new(error),
})),
}
}
}
impl From<JinjaError> for Error {
#[inline]
fn from(error: JinjaError) -> Error {
Error {
repr: Repr::Custom(Box::new(Custom {
kind: ErrorKind::JinjaRenderError,
error: Box::new(error),
})),
}
}
}
impl Error {
pub fn new<E>(kind: ErrorKind, error: E) -> Error
where
E: Into<Box<dyn StdError + Send + Sync>>,
{
Self::_new(kind, error.into())
}
fn _new(kind: ErrorKind, error: Box<dyn StdError + Send + Sync>) -> Error {
Error {
repr: Repr::Custom(Box::new(Custom { kind, error })),
}
}
pub fn kind(&self) -> ErrorKind {
match self.repr {
Repr::Custom(ref c) => c.kind,
Repr::Simple(kind) => kind,
}
}
pub fn raw_os_error(&self) -> Option<i32> {
match self.repr {
Repr::Custom(..) => None,
Repr::Simple(..) => None,
}
}
pub fn get_ref(&self) -> Option<&(dyn StdError + Send + Sync + 'static)> {
match self.repr {
Repr::Simple(..) => None,
Repr::Custom(ref c) => Some(&*c.error),
}
}
pub fn get_mut(&mut self) -> Option<&mut (dyn StdError + Send + Sync + 'static)> {
match self.repr {
Repr::Simple(..) => None,
Repr::Custom(ref mut c) => Some(&mut *c.error),
}
}
pub fn into_inner(self) -> Option<Box<dyn StdError + Send + Sync>> {
match self.repr {
Repr::Simple(..) => None,
Repr::Custom(c) => Some(c.error),
}
}
}
impl fmt::Debug for Repr {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt),
Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.repr {
Repr::Custom(ref c) => c.error.fmt(fmt),
Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()),
}
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self.repr {
Repr::Simple(..) => None,
Repr::Custom(ref c) => c.error.source(),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use std::fmt;
#[test]
fn test_debug_error() {
let err = Error {
repr: Repr::Custom(Box::new(Custom {
kind: ErrorKind::InvalidData,
error: Box::new(Error {
repr: super::Repr::Custom(Box::new(Custom {
kind: ErrorKind::Other,
error: Box::new(Error::new(ErrorKind::Other, "oh no!")),
})),
}),
})),
};
let expected = "\
Custom { \
kind: InvalidData, \
error: Custom { \
kind: Other, \
error: Custom { \
kind: Other, \
error: \"oh no!\" \
} \
} \
}";
assert_eq!(format!("{err:?}"), expected);
}
#[test]
fn test_downcasting() {
#[derive(Debug)]
struct TestError;
impl fmt::Display for TestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("asdf")
}
}
impl StdError for TestError {}
let mut err = Error::new(ErrorKind::Other, TestError);
assert!(err.get_ref().unwrap().is::<TestError>());
assert_eq!("asdf", err.get_ref().unwrap().to_string());
assert!(err.get_mut().unwrap().is::<TestError>());
let extracted = err.into_inner().unwrap();
extracted.downcast::<TestError>().unwrap();
}
}