diff --git a/examples/auth-with-state.rs b/examples/auth-with-state.rs index 95f01fe..27768c9 100644 --- a/examples/auth-with-state.rs +++ b/examples/auth-with-state.rs @@ -34,7 +34,10 @@ async fn validate_authtoken( authstate.authed = Some(state.clone().lock().await.authtoken == token); Ok((req, resp, authstate)) } else { - Err(Error::StatusCode(StatusCode::UNAUTHORIZED)) + Err(Error::StatusCode( + StatusCode::UNAUTHORIZED, + String::default(), + )) } } @@ -66,7 +69,10 @@ async fn hello( )); } - Err(Error::StatusCode(StatusCode::UNAUTHORIZED)) + Err(Error::StatusCode( + StatusCode::UNAUTHORIZED, + String::default(), + )) } // Our global application state; must be `Clone`. diff --git a/examples/disk-auth.rs b/examples/disk-auth.rs index 26d478a..84d6900 100644 --- a/examples/disk-auth.rs +++ b/examples/disk-auth.rs @@ -12,7 +12,7 @@ async fn validate_authtoken( ) -> HTTPResult { let token = req.headers().get("X-AuthToken"); if token.is_none() { - return Err(Error::StatusCode(StatusCode::UNAUTHORIZED)); + return Err(Error::StatusCode(StatusCode::UNAUTHORIZED, String::new())); } let token = token.unwrap(); @@ -26,7 +26,7 @@ async fn validate_authtoken( }; if !matches { - return Err(Error::StatusCode(StatusCode::UNAUTHORIZED)); + return Err(Error::StatusCode(StatusCode::UNAUTHORIZED, String::new())); } return Ok((req, resp, NoState {})); diff --git a/src/app.rs b/src/app.rs index d14178a..2355c24 100644 --- a/src/app.rs +++ b/src/app.rs @@ -142,10 +142,10 @@ impl App< pub async fn dispatch(&self, req: Request) -> Result, Infallible> { match self.router.dispatch(req, self.clone()).await { Ok(resp) => Ok(resp), - Err(e) => match e { - Error::StatusCode(sc) => Ok(Response::builder() + Err(e) => match e.clone() { + Error::StatusCode(sc, msg) => Ok(Response::builder() .status(sc) - .body(Body::default()) + .body(Body::from(msg)) .unwrap()), Error::InternalServerError(e) => Ok(Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) @@ -181,12 +181,13 @@ impl App< } } -pub struct TestService { +#[derive(Clone)] +pub struct TestApp { app: App, headers: Option, } -impl TestService { +impl TestApp { pub fn new(app: App) -> Self { Self { app, headers: None } } diff --git a/src/lib.rs b/src/lib.rs index bca3bc4..c01c73f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,7 @@ where /// [http::Response] returns. #[derive(Clone, Debug)] pub enum Error { - StatusCode(http::StatusCode), + StatusCode(http::StatusCode, String), InternalServerError(String), } @@ -54,9 +54,12 @@ impl Error { Self::InternalServerError(message.to_string()) } - /// A convenient way to return status codes. - pub fn new_status(error: http::StatusCode) -> Self { - Self::StatusCode(error) + /// A convenient way to return status codes with optional informational bodies. + pub fn new_status(error: http::StatusCode, message: T) -> Self + where + T: ToString, + { + Self::StatusCode(error, message.to_string()) } } @@ -69,6 +72,13 @@ where } } +pub trait ToStatus +where + Self: ToString, +{ + fn to_status(&self) -> Error; +} + /// HTTPResult is the return type for handlers. If a handler terminates at the end of its chain /// with [std::option::Option::None] as the [http::Response], a 500 Internal Server Error will be /// returned. If you wish to return Err(), a [http::StatusCode] or [std::string::String] can be @@ -111,7 +121,8 @@ impl TransientState for NoState { /// ``` pub mod prelude { pub use crate::{ - app::App, compose_handler, Error, HTTPResult, NoState, Params, ServerError, TransientState, + app::App, compose_handler, Error, HTTPResult, NoState, Params, ServerError, ToStatus, + TransientState, }; pub use http::{Request, Response, StatusCode}; pub use hyper::Body; diff --git a/src/router.rs b/src/router.rs index 2f33198..f60ab7b 100644 --- a/src/router.rs +++ b/src/router.rs @@ -52,7 +52,10 @@ impl Route { let params = self.path.extract(provided)?; if self.method != req.method() { - return Err(Error::StatusCode(http::StatusCode::NOT_FOUND)); + return Err(Error::StatusCode( + http::StatusCode::NOT_FOUND, + String::new(), + )); } self.handler.perform(req, None, params, app, state).await @@ -85,14 +88,20 @@ impl Router { .dispatch(path.to_string(), req, app, T::initial()) .await?; if response.is_none() { - return Err(Error::StatusCode(http::StatusCode::INTERNAL_SERVER_ERROR)); + return Err(Error::StatusCode( + http::StatusCode::INTERNAL_SERVER_ERROR, + String::new(), + )); } return Ok(response.unwrap()); } } - Err(Error::StatusCode(http::StatusCode::NOT_FOUND)) + Err(Error::StatusCode( + http::StatusCode::METHOD_NOT_ALLOWED, + String::new(), + )) } }