easy-err 1.0.2

An easy Error wrapper.
Documentation
use std::{
    error, fmt,
    fs::OpenOptions,
    io::{self, Write},
    path::Path,
};

pub use chrono::{DateTime, Local};
use colored::Colorize;

/// 用于统一错误的枚举
///
/// # Examples
/// ```
/// let err = easy_err::Error::custom("Here is a error.");
/// err.report();
/// ```
#[derive(Debug)]
pub enum Error {
    /// 用于包装已经实现Error Trait的结构体或枚举
    Basic(DateTime<Local>, Box<dyn error::Error>),
    /// 用于报告用户自定义的错误信息
    Custom(DateTime<Local>, String),
}

impl Error {
    /// 获取错误发生的时间
    ///
    /// # Example
    /// ```
    /// let time = easy_err::Error::custom("Here is a custom error.").time();
    /// println!("{}", time);
    /// ```
    pub fn time(&self) -> DateTime<Local> {
        match self {
            Self::Basic(time, _) => *time,
            Self::Custom(time, _) => *time,
        }
    }

    /// 产生一个basic变体
    ///
    /// # Examples
    /// ```
    /// let file = std::fs::File::open("An obvious mistake.").or_else(|err|{Err(easy_err::Error::basic(err))});
    /// match file {
    ///     Ok(_) => println!("Ok!"),
    ///     Err(err) => {err.report();}
    /// }
    /// ```
    pub fn basic(err: impl error::Error + 'static) -> Error {
        Error::Basic(Local::now(), Box::new(err))
    }

    /// 产生一个custom变体
	/// 
	/// # Note
	/// 推荐使用 `custom!()` 宏代替
    ///
    /// # Examples
    /// ```
    /// let custom_err = easy_err::Error::custom("Here is a error.");
    /// custom_err.report();
    /// ```
    pub fn custom<S>(msg: S) -> Error
    where
        S: ToString,
    {
        Error::Custom(Local::now(), msg.to_string())
    }

    /// 在标准错误流中输出一条报告信息
    ///
    /// # Examples
    /// ```
    /// let custom_err = easy_err::Error::custom("Here is a error.");
    /// custom_err.report();
    /// // output:
    /// // error:
    /// //     time: <ErrorTime>
    /// //      msg: 'Here is a error.'
    /// ```
    pub fn report(&self) -> &Self {
        eprintln!("\n{}:{}", "error".red().bold(), self);
        self
    }

    /// 引发 panic!() 宏
    ///
    /// # Examples
    /// ```
    /// let custom_err = easy_err::Error::custom("Here is a error.");
    /// custom_err.panic();
    /// // output:
    /// // error:
    /// //     time: <ErrorTime>
    /// //      msg: 'Here is a error.'
    /// ```
    pub fn panic(&self) {
        panic!("{}", self);
    }

    /// 将报告信息输出至文件
    ///
    /// # Params
    /// - path: 任何可以转换为 Path 的值
    ///
    /// # Examples
    /// ```
    /// let custom_err = easy_err::Error::custom("Here is a error.");
    /// custom_err.log_to("./log.txt");
    /// ```
    pub fn log_to<P>(&self, path: P) -> &Self
    where
        P: AsRef<Path>,
    {
        match OpenOptions::new().create(true).append(true).open(path) {
            Ok(mut file) => {
                if let Err(err) = writeln!(file, "error:{}", self) {
                    Error::basic(err).report();
                }
            }
            Err(err) => {
                Error::basic(err).report();
            }
        }
        self
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Basic(time, err) => write!(f, "\n\t time: {}\n\t  msg: {}\n", time, err),
            Self::Custom(time, msg) => write!(f, "\n\t time: {}\n\t  msg: {}\n", time, msg),
        }
    }
}

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match self {
            Self::Basic(.., err) => Some(err.as_ref()),
            Self::Custom(..) => None,
        }
    }
}

impl From<io::Error> for Error {
    fn from(value: io::Error) -> Self {
        Self::basic(value)
    }
}

impl From<fmt::Error> for Error {
    fn from(value: fmt::Error) -> Self {
        Self::basic(value)
    }
}

impl From<&Self> for Error {
	fn from(value: &Self) -> Self {
		match value {
			Error::Basic( time, err ) => Error::Basic(*time, Box::new(Error::custom(err.to_string()))),
			Error::Custom(time, msg) => Error::Custom(*time, msg.to_string())

		}
	}
}

/// 用于方便地将Result<T, Error>当作Error处理
///
/// # Tips
/// - 不建议为别的结构体或枚举实现此Trait,因为我自己还没完全搞清楚这个地方该怎么弄,这暂且还是一个实验性功能
///
/// # Example
/// ```
/// use std::fs;
/// use easy_err::{Error, ErrResult};
///
/// fn test() -> Result<(), Error> {
///     fs::File::open("An obvious mistake.")?;
///     Ok(())
/// }
///
/// test().panic();
/// ```
pub trait ErrResult<T> {
    fn panic(self) -> T;
}

impl<T> ErrResult<T> for Result<T, Error> {
    fn panic(self) -> T {
        match self {
            Ok(t) => t,
            Err(err) => panic!("{}", err.to_string()),
        }
    }
}