pub use http::StatusCode;
use std::fmt::{Display, Formatter};
use std::future::Future;
use std::pin::Pin;
use std::result::Result as StdResult;
pub type ResultFuture<'a, R = ()> = Pin<Box<dyn 'a + Future<Output = Result<R>>>>;
pub type Result<R = ()> = StdResult<R, Error>;
#[macro_export]
macro_rules! throw {
($status_code:expr) => {
$crate::throw!($status_code, "");
};
($status_code:expr, $message:expr) => {
$crate::throw!($status_code, $message, true);
};
($status_code:expr, $message:expr, $expose:expr) => {
return Err($crate::Error::new($status_code, $message, $expose));
};
}
#[derive(Debug, Clone)]
pub struct Error {
pub status_code: StatusCode,
pub kind: ErrorKind,
pub message: String,
pub expose: bool,
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum ErrorKind {
Informational,
Redirection,
ClientError,
ServerError,
}
impl ErrorKind {
#[inline]
fn infer(status_code: StatusCode) -> Self {
use ErrorKind::*;
match status_code.as_u16() / 100 {
1 => Informational,
3 => Redirection,
4 => ClientError,
5 => ServerError,
_ => panic!(
r"status {} cannot be thrown.
Please use `ctx.resp_mut().await.status = xxx` to set it.
",
status_code
),
}
}
}
impl Error {
#[inline]
pub fn new(status_code: StatusCode, message: impl ToString, expose: bool) -> Self {
Self {
status_code,
kind: ErrorKind::infer(status_code),
message: message.to_string(),
expose,
}
}
#[inline]
pub(crate) fn need_throw(&self) -> bool {
self.kind == ErrorKind::ServerError
}
}
macro_rules! internal_server_error {
($error:ty) => {
impl From<$error> for Error {
#[inline]
fn from(err: $error) -> Self {
Self::new(StatusCode::INTERNAL_SERVER_ERROR, err, false)
}
}
};
}
internal_server_error!(std::io::Error);
internal_server_error!(http::Error);
impl Display for Error {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> StdResult<(), std::fmt::Error> {
f.write_str(&format!("{}: {}", self.status_code, self.message))
}
}
impl std::error::Error for Error {}