zero4rs 2.0.0

zero4rs is a powerful, pragmatic, and extremely fast web framework for Rust
Documentation
pub use std::error::Error as _;

use actix_http::StatusCode;
use actix_web::{HttpResponse, ResponseError};

use super::R;

// 全局错误码
pub const UNAUTH_ACCESS: u16 = 401;
pub const INVALID_PARAMS: u16 = 413;
pub const INVALID_REQUEST: u16 = 400;
pub const NOT_FOUND_CODE: u16 = 404;
pub const INTERNAL_SERVER_ERROR: u16 = 500;

// pub type Error = Box<dyn std::error::Error>;
pub type Result<T> = core::result::Result<T, Error>;

#[derive(Debug, thiserror::Error)]
pub enum Error2 {
    #[error("GenericError: error={}", .0)]
    GenericError(String),
    #[error("InvalidRequestError: error={}", .0)]
    InvalidRequest(String),
    #[error("InvalidParam: error={}", .0)]
    InvalidParam(String),
}

#[derive(thiserror::Error)]
pub enum Error {
    #[error("InvalidCredentials: error={}", .0)]
    InvalidCredentials(String),
    #[error("InvalidParam: error={}", .0)]
    ObjectIdError(#[from] bson::error::Error),
    #[error("ReqwestError: error={}", .0)]
    ReqwestError(#[from] reqwest::Error),
    #[error("IOError: error={}", .0)]
    IOError(#[from] std::io::Error),
    #[error("SerdeError: error={}", .0)]
    SerdeError(#[from] serde_json::Error),
    #[error("SQLError: error={}", .0)]
    SQLError(#[from] sqlx_core::Error),
    #[error(transparent)]
    UnexpectedError(#[from] anyhow::Error),
}

impl Error {
    pub fn real_error(&self) -> u16 {
        match self {
            Error::InvalidCredentials(e) => {
                log::error!("InvalidCredentials: code={}, error={:?}", UNAUTH_ACCESS, e);

                UNAUTH_ACCESS
            }
            Error::ObjectIdError(e) => {
                log::error!(
                    "ObjectIdError: code={}, error={:?}",
                    INTERNAL_SERVER_ERROR,
                    e
                );

                INTERNAL_SERVER_ERROR
            }
            Error::ReqwestError(e) => {
                log::error!(
                    "ReqwestError: code={}, error={:?}",
                    INTERNAL_SERVER_ERROR,
                    e
                );

                INTERNAL_SERVER_ERROR
            }
            Error::IOError(e) => {
                log::error!("IOError: code={}, error={:?}", INTERNAL_SERVER_ERROR, e);

                INTERNAL_SERVER_ERROR
            }
            Error::SQLError(e) => {
                log::error!("SQLError: code={}, error={:?}", INTERNAL_SERVER_ERROR, e);

                INTERNAL_SERVER_ERROR
            }
            Error::SerdeError(e) => {
                log::error!("SerdeError: code={}, error={:?}", INTERNAL_SERVER_ERROR, e);

                INTERNAL_SERVER_ERROR
            }
            Error::UnexpectedError(e) => {
                if let Some(error2) = e.downcast_ref::<Error2>() {
                    return match error2 {
                        Error2::GenericError(_) => INTERNAL_SERVER_ERROR,
                        Error2::InvalidRequest(_) => INVALID_REQUEST,
                        Error2::InvalidParam(_) => INVALID_PARAMS,
                    };
                }

                log::error!(
                    "UnexpectedError: code={}, error={:?}",
                    INTERNAL_SERVER_ERROR,
                    e
                );

                INTERNAL_SERVER_ERROR
            }
        }
    }

    pub fn run_time<T>(e: T) -> Self
    where
        T: std::fmt::Debug,
    {
        Self::UnexpectedError(anyhow::anyhow!(format!("run_time: error={:?}", e)))
    }

    pub fn throw<T>(msg: impl Into<String>, e: Option<T>) -> Self
    where
        T: std::fmt::Debug,
    {
        Self::UnexpectedError(anyhow::anyhow!(format!(
            "throw-{}: error={:?}",
            msg.into(),
            e
        )))
    }

    pub fn invalid_request(msg: impl Into<String>) -> Self {
        Self::UnexpectedError(anyhow::anyhow!(Error2::InvalidRequest(msg.into())))
    }
}

// Error Boilerplate
impl std::fmt::Debug for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let e = self;

        writeln!(f, "{}\n", e)?;

        let mut current = e.source();

        while let Some(cause) = current {
            writeln!(f, "Caused by:\n\t{}", cause)?;
            current = cause.source();
        }

        Ok(())
    }
}

impl ResponseError for Error {
    fn error_response(&self) -> HttpResponse {
        HttpResponse::build(StatusCode::OK)
            .content_type("application/json")
            .body(serde_json::to_string(&R::failed(self.real_error(), self.to_string())).unwrap())
    }
}