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())?,
))
}
#[allow(clippy::missing_panics_doc)]
pub fn empty_status(status: http::StatusCode) -> Self {
Response(
http::Response::builder()
.status(status)
.body(hyper::Body::empty())
.unwrap(),
)
}
#[allow(clippy::missing_panics_doc)]
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(),
)
}
#[cfg(feature = "json")]
#[allow(clippy::missing_panics_doc)]
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)
}
pub fn state<T: Send + Sync + 'static>(&self) -> Option<&T> {
self.ext::<crate::middleware::State<T>>().map(|v| &v.0)
}
pub fn ext<T: Send + Sync + 'static>(&self) -> Option<&T> {
self.extensions().get::<T>()
}
pub fn ext_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut T> {
self.extensions_mut().get_mut::<T>()
}
pub fn set_ext<T: Send + Sync + 'static>(&mut self, value: T) -> &mut Self {
self.extensions_mut().insert(value);
self
}
pub fn with_ext<T: Send + Sync + 'static>(mut self, value: T) -> Self {
self.set_ext(value);
self
}
pub fn remove_ext<T: Send + Sync + 'static>(&mut self) -> Option<T> {
self.extensions_mut().remove::<T>()
}
pub fn without_ext<T: Send + Sync + 'static>(mut self) -> Self {
self.remove_ext::<T>();
self
}
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>;
}
}
impl crate::HttpEntity for Response {
#[inline]
fn body_mut(&mut self) -> &mut hyper::Body {
self.0.body_mut()
}
#[inline]
fn headers(&self) -> &http::HeaderMap<http::HeaderValue> {
self.0.headers()
}
#[inline]
fn headers_mut(&mut self) -> &mut http::HeaderMap<http::HeaderValue> {
self.0.headers_mut()
}
}
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
}
}
impl std::borrow::Borrow<http::Response<hyper::Body>> for Response {
fn borrow(&self) -> &http::Response<hyper::Body> {
&self.0
}
}
impl std::borrow::BorrowMut<http::Response<hyper::Body>> for Response {
fn borrow_mut(&mut self) -> &mut http::Response<hyper::Body> {
&mut self.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());
}
}