mod exception_handler_trait;
mod exception_handler_wrapper;
use crate::headers::ContentType;
use crate::{Response, StatusCode};
pub(crate) use exception_handler_trait::ExceptionHandler;
pub(crate) use exception_handler_wrapper::ExceptionHandlerWrapper;
use serde::Serialize;
use serde_json::Value;
use std::backtrace::Backtrace;
use std::io;
use thiserror::Error;
pub type BoxedError = Box<dyn std::error::Error + Send + Sync>;
#[derive(Error, Debug)]
pub enum SilentError {
#[error("io error")]
IOError(#[from] io::Error),
#[cfg(feature = "upgrade")]
#[error("io error")]
TungsteniteError(#[from] tokio_tungstenite::tungstenite::Error),
#[error("serde_json error `{0}`")]
SerdeJsonError(#[from] serde_json::Error),
#[error("serde de error `{0}`")]
SerdeDeError(#[from] serde::de::value::Error),
#[error("the data for key `{0}` is not available")]
HyperError(#[from] hyper::Error),
#[cfg(feature = "multipart")]
#[error("upload file read error `{0}`")]
FileEmpty(#[from] multer::Error),
#[error("body is empty")]
BodyEmpty,
#[error("json is empty")]
JsonEmpty,
#[error("content-type is error")]
ContentTypeError,
#[error("params is empty")]
ParamsEmpty,
#[error("params not found")]
ParamsNotFound,
#[error("config not found")]
ConfigNotFound,
#[error("websocket error: {0}")]
WsError(String),
#[error("{0}")]
AnyhowError(#[from] anyhow::Error),
#[error("business error: {msg} ({code})")]
BusinessError {
code: StatusCode,
msg: String,
},
}
pub type SilentResult<T> = Result<T, SilentError>;
impl From<(StatusCode, String)> for SilentError {
fn from(value: (StatusCode, String)) -> Self {
Self::business_error(value.0, value.1)
}
}
impl From<(u16, String)> for SilentError {
fn from(value: (u16, String)) -> Self {
Self::business_error(
StatusCode::from_u16(value.0).expect("invalid status code"),
value.1,
)
}
}
impl From<String> for SilentError {
fn from(value: String) -> Self {
Self::business_error(StatusCode::INTERNAL_SERVER_ERROR, value)
}
}
impl From<BoxedError> for SilentError {
fn from(value: BoxedError) -> Self {
Self::business_error(StatusCode::INTERNAL_SERVER_ERROR, value.to_string())
}
}
impl SilentError {
pub fn business_error_obj<S>(code: StatusCode, msg: S) -> Self
where
S: Serialize,
{
let msg = serde_json::to_string(&msg).unwrap_or_default();
Self::BusinessError { code, msg }
}
pub fn business_error(code: StatusCode, msg: String) -> Self {
Self::BusinessError { code, msg }
}
pub fn status(&self) -> StatusCode {
match self {
Self::BusinessError { code, .. } => *code,
Self::SerdeDeError(_) => StatusCode::UNPROCESSABLE_ENTITY,
Self::SerdeJsonError(_) => StatusCode::UNPROCESSABLE_ENTITY,
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
pub fn message(&self) -> String {
match self {
Self::BusinessError { msg, .. } => msg.clone(),
Self::SerdeDeError(e) => e.to_string(),
Self::SerdeJsonError(e) => e.to_string(),
_ => self.to_string(),
}
}
pub fn trace(&self) -> Backtrace {
Backtrace::capture()
}
}
impl From<SilentError> for Response {
fn from(value: SilentError) -> Self {
let mut res = Response::empty();
res.set_status(value.status());
if serde_json::from_str::<Value>(&value.message()).is_ok() {
res.set_typed_header(ContentType::json());
}
res.set_body(value.message().into());
res
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Response;
use http_body_util::BodyExt;
use hyper::StatusCode;
use serde_json::Value;
#[derive(Serialize)]
struct ResBody {
code: u16,
msg: String,
data: Value,
}
#[tokio::test]
async fn test_silent_error() {
let res_body = ResBody {
code: 400,
msg: "bad request".to_string(),
data: Value::Null,
};
let err = SilentError::business_error_obj(StatusCode::BAD_REQUEST, res_body);
let mut res: Response = err.into();
assert_eq!(res.status, StatusCode::BAD_REQUEST);
println!("{:#?}", res.headers);
println!(
"{:#?}",
res.body.frame().await.unwrap().unwrap().data_ref().unwrap()
);
}
}