use std::convert::TryFrom;
#[derive(Debug)]
#[must_use]
pub struct Response(http::Response<hyper::Body>);
macro_rules! forward {
() => {};
(
$(#[$m:meta])* $v:vis fn $name:ident(&self $(, $pn:ident: $pt:ty)*) -> $ret:ty;
$($tail:tt)*
) => {
$(#[$m])* $v fn $name(&self $(, $pn: $pt)*) -> $ret {
(self.0).$name($($pn),*)
}
forward! { $($tail)* }
};
(
$(#[$m:meta])* $v:vis fn $name:ident(&mut self $(, $pn:ident: $pt:ty)*) -> $ret:ty;
$($tail:tt)*
) => {
$(#[$m])* $v fn $name(&mut self $(, $pn: $pt)*) -> $ret {
(self.0).$name($($pn),*)
}
forward! { $($tail)* }
}
}
impl Response {
pub fn empty_200() -> Self {
Self::empty_status(http::StatusCode::OK)
}
pub fn empty_204() -> Self {
Response::empty_status(http::StatusCode::NO_CONTENT)
}
pub fn empty_404() -> Self {
Response::empty_status(http::StatusCode::NOT_FOUND)
}
pub fn empty_500() -> Self {
Response::empty_status(http::StatusCode::INTERNAL_SERVER_ERROR)
}
pub fn see_other<T>(location: T) -> Result<Self, http::Error>
where
http::HeaderValue: TryFrom<T>,
<http::HeaderValue as TryFrom<T>>::Error: Into<http::Error>,
{
Ok(Response(
http::Response::builder()
.status(http::StatusCode::SEE_OTHER)
.header(http::header::LOCATION, location)
.body(hyper::Body::empty())?,
))
}
pub fn permanent_redirect<T>(location: T) -> Result<Self, http::Error>
where
http::HeaderValue: TryFrom<T>,
<http::HeaderValue as TryFrom<T>>::Error: Into<http::Error>,
{
Ok(Response(
http::Response::builder()
.status(http::StatusCode::PERMANENT_REDIRECT)
.header(http::header::LOCATION, location)
.body(hyper::Body::empty())?,
))
}
pub fn temporary_redirect<T>(location: T) -> Result<Self, http::Error>
where
http::HeaderValue: TryFrom<T>,
<http::HeaderValue as TryFrom<T>>::Error: Into<http::Error>,
{
Ok(Response(
http::Response::builder()
.status(http::StatusCode::TEMPORARY_REDIRECT)
.header(http::header::LOCATION, location)
.body(hyper::Body::empty())?,
))
}
pub fn empty_status(status: http::StatusCode) -> Self {
Response(
http::Response::builder()
.status(status)
.body(hyper::Body::empty())
.unwrap(),
)
}
pub fn text<V: Into<String>>(body: V) -> Self {
Response(
http::Response::builder()
.header(http::header::CONTENT_TYPE, "text/plain; charset=utf-8")
.body(body.into().into())
.unwrap(),
)
}
pub fn json<V: serde::Serialize>(body: &V) -> Result<Self, serde_json::Error> {
let value = serde_json::to_string(body)?;
Ok(Response(
http::Response::builder()
.header(
http::header::CONTENT_TYPE,
"application/json; charset=utf-8",
)
.body(value.into())
.unwrap(),
))
}
pub fn set_status<S: Into<http::StatusCode>>(&mut self, status: S) {
*self.0.status_mut() = status.into();
}
pub fn with_status<S: Into<http::StatusCode>>(mut self, status: S) -> Self {
*self.0.status_mut() = status.into();
Response(self.0)
}
#[doc(hidden)]
pub fn body_mut(&mut self) -> &mut hyper::Body {
self.0.body_mut()
}
forward! {
pub fn status(&self) -> http::StatusCode;
pub fn extensions(&self) -> &http::Extensions;
pub fn extensions_mut(&mut self) -> &mut http::Extensions;
pub fn headers(&self) -> &http::HeaderMap<http::HeaderValue>;
pub fn headers_mut(&mut self) -> &mut http::HeaderMap<http::HeaderValue>;
}
}
has_body!(Response);
has_extensions!(Response);
has_headers!(Response);
impl Default for Response {
fn default() -> Self {
Response(
http::Response::builder()
.body(hyper::Body::empty())
.unwrap(),
)
}
}
impl From<http::Response<hyper::Body>> for Response {
fn from(hy: http::Response<hyper::Body>) -> Self {
Response(hy)
}
}
impl From<Response> for http::Response<hyper::Body> {
fn from(this: Response) -> Self {
this.0
}
}
pub trait IntoResponse {
fn into_response(self) -> Result<Response, anyhow::Error>;
}
impl IntoResponse for Response {
fn into_response(self) -> Result<Response, anyhow::Error> {
Ok(self)
}
}
impl<R, E> IntoResponse for Result<R, E>
where
R: IntoResponse,
E: Into<anyhow::Error>,
{
fn into_response(self) -> Result<Response, anyhow::Error> {
self.map_err(Into::into)
.and_then(|r| r.into_response().map_err(Into::into))
}
}
impl IntoResponse for std::convert::Infallible {
fn into_response(self) -> Result<Response, anyhow::Error> {
match self {}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn convert_response() {
let response = Response::empty_500();
assert!(Ok::<_, std::convert::Infallible>(response)
.into_response()
.is_ok());
}
}