use core::{
convert::Infallible,
fmt,
ops::{Deref, DerefMut},
};
use std::{error, io};
pub use xitca_http::{
error::BodyError,
util::service::{
route::MethodNotAllowed,
router::{MatchError, RouterError},
},
};
use crate::{
body::ResponseBody,
context::WebContext,
http::{
header::{InvalidHeaderValue, ALLOW},
StatusCode, WebResponse,
},
service::Service,
};
use self::service_impl::ErrorService;
pub struct Error<C = ()>(Box<dyn for<'r> ErrorService<WebContext<'r, C>>>);
impl<C> Error<C> {
pub fn from_service<S>(s: S) -> Self
where
S: for<'r> Service<WebContext<'r, C>, Response = WebResponse, Error = Infallible>
+ error::Error
+ Send
+ Sync
+ 'static,
{
Self(Box::new(s))
}
}
impl<C> fmt::Debug for Error<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl<C> fmt::Display for Error<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl<C> error::Error for Error<C> {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.0.source()
}
}
impl<C> Deref for Error<C> {
type Target = dyn for<'r> ErrorService<WebContext<'r, C>>;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl<C> DerefMut for Error<C> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.0
}
}
impl<'r, C> Service<WebContext<'r, C>> for Error<C> {
type Response = WebResponse;
type Error = Infallible;
async fn call(&self, ctx: WebContext<'r, C>) -> Result<Self::Response, Self::Error> {
crate::service::object::ServiceObject::call(self.deref(), ctx).await
}
}
macro_rules! error_from_service {
($tt: ty) => {
impl<C> From<$tt> for Error<C> {
fn from(e: $tt) -> Self {
Self::from_service(e)
}
}
};
}
pub(crate) use error_from_service;
macro_rules! blank_error_service {
($type: ty, $status: path) => {
impl<'r, C, B> Service<WebContext<'r, C, B>> for $type {
type Response = WebResponse;
type Error = Infallible;
async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
let mut res = ctx.into_response(ResponseBody::empty());
*res.status_mut() = $status;
Ok(res)
}
}
};
}
macro_rules! forward_blank_internal {
($type: ty) => {
impl<'r, C, B> crate::service::Service<WebContext<'r, C, B>> for $type {
type Response = WebResponse;
type Error = Infallible;
async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
crate::error::Internal.call(ctx).await
}
}
};
}
pub(crate) use forward_blank_internal;
macro_rules! forward_blank_bad_request {
($type: ty) => {
impl<'r, C, B> crate::service::Service<WebContext<'r, C, B>> for $type {
type Response = WebResponse;
type Error = ::core::convert::Infallible;
async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
crate::error::BadRequest.call(ctx).await
}
}
};
}
pub(crate) use forward_blank_bad_request;
impl<C> From<Infallible> for Error<C> {
fn from(e: Infallible) -> Self {
match e {}
}
}
impl<'r, C, B> Service<WebContext<'r, C, B>> for Infallible {
type Response = WebResponse;
type Error = Infallible;
async fn call(&self, _: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
unreachable!()
}
}
pub struct ErrorStatus(StatusCode);
impl fmt::Debug for ErrorStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl fmt::Display for ErrorStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl error::Error for ErrorStatus {}
impl<C> From<StatusCode> for Error<C> {
fn from(e: StatusCode) -> Self {
Error::from_service(ErrorStatus(e))
}
}
impl<'r, C, B> Service<WebContext<'r, C, B>> for ErrorStatus {
type Response = WebResponse;
type Error = Infallible;
async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
let mut res = ctx.into_response(ResponseBody::empty());
*res.status_mut() = self.0;
Ok(res)
}
}
error_from_service!(io::Error);
forward_blank_internal!(io::Error);
error_from_service!(MatchError);
blank_error_service!(MatchError, StatusCode::NOT_FOUND);
error_from_service!(MethodNotAllowed);
error_from_service!(InvalidHeaderValue);
forward_blank_bad_request!(InvalidHeaderValue);
impl<'r, C, B> Service<WebContext<'r, C, B>> for MethodNotAllowed {
type Response = WebResponse;
type Error = Infallible;
async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
let mut res = ctx.into_response(ResponseBody::empty());
let allowed = self.allowed_methods();
let len = allowed.iter().fold(0, |a, m| a + m.as_str().len() + 1);
let mut methods = String::with_capacity(len);
for method in allowed {
methods.push_str(method.as_str());
methods.push(',');
}
methods.pop();
res.headers_mut().insert(ALLOW, methods.parse().unwrap());
*res.status_mut() = StatusCode::METHOD_NOT_ALLOWED;
Ok(res)
}
}
impl<E, C> From<RouterError<E>> for Error<C>
where
E: Into<Self>,
{
fn from(e: RouterError<E>) -> Self {
match e {
RouterError::Match(e) => e.into(),
RouterError::NotAllowed(e) => e.into(),
RouterError::Service(e) => e.into(),
}
}
}
type StdErr = Box<dyn error::Error + Send + Sync>;
impl<C> From<StdErr> for Error<C> {
fn from(e: StdErr) -> Self {
Self(Box::new(StdError(e)))
}
}
forward_blank_internal!(StdErr);
pub struct StdError(pub StdErr);
impl fmt::Debug for StdError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl fmt::Display for StdError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl error::Error for StdError {}
impl<'r, C, B> Service<WebContext<'r, C, B>> for StdError {
type Response = WebResponse;
type Error = Infallible;
async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
self.0.call(ctx).await
}
}
#[derive(Debug)]
pub struct Internal;
impl fmt::Display for Internal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Internal error")
}
}
impl error::Error for Internal {}
error_from_service!(Internal);
blank_error_service!(Internal, StatusCode::INTERNAL_SERVER_ERROR);
#[derive(Debug)]
pub struct BadRequest;
impl fmt::Display for BadRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Bad request")
}
}
impl error::Error for BadRequest {}
error_from_service!(BadRequest);
blank_error_service!(BadRequest, StatusCode::BAD_REQUEST);
mod service_impl {
use crate::service::object::ServiceObject;
use super::*;
pub trait ErrorService<Req>:
ServiceObject<Req, Response = WebResponse, Error = Infallible> + error::Error + Send + Sync
{
}
impl<S, Req> ErrorService<Req> for S where
S: ServiceObject<Req, Response = WebResponse, Error = Infallible> + error::Error + Send + Sync
{
}
}
#[cfg(test)]
mod test {
use core::fmt;
use xitca_unsafe_collection::futures::NowOrPanic;
use crate::body::ResponseBody;
use super::*;
#[test]
fn cast() {
#[derive(Debug)]
struct Foo;
impl fmt::Display for Foo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Foo")
}
}
impl error::Error for Foo {}
impl<'r, C> Service<WebContext<'r, C>> for Foo {
type Response = WebResponse;
type Error = Infallible;
async fn call(&self, _: WebContext<'r, C>) -> Result<Self::Response, Self::Error> {
Ok(WebResponse::new(ResponseBody::None))
}
}
let foo = Error::<()>::from_service(Foo);
println!("{foo:?}");
println!("{foo}");
let mut ctx = WebContext::new_test(());
let res = Service::call(&foo, ctx.as_web_ctx()).now_or_panic().unwrap();
assert_eq!(res.status().as_u16(), 200);
}
}