use std::num::ParseIntError;
use headers::{ContentType, HeaderMapExt};
use http::{HeaderName, HeaderValue, StatusCode};
use http_body_util::Full;
use hyper::body::Bytes;
use serde::Serialize;
pub type Response = http::Response<Full<Bytes>>;
pub struct Json<T>(pub T);
pub fn json<T: Serialize>(data: T) -> Json<T> {
Json(data)
}
pub trait IntoResponse {
fn into_response(self) -> Response;
fn into_json_response(self) -> Response
where
Self: Sized,
{
let mut res = self.into_response();
let headers = res.headers_mut();
headers.typed_insert(ContentType::json());
res
}
fn into_html_response(self) -> Response
where
Self: Sized,
{
let mut res = self.into_response();
let headers = res.headers_mut();
headers.typed_insert(ContentType::html());
res
}
}
impl IntoResponse for Response {
fn into_response(self) -> Response {
self
}
}
impl IntoResponse for &'static str {
fn into_response(self) -> Response {
let mut res = Response::new(self.into());
let headers = res.headers_mut();
headers.typed_insert(ContentType::html());
res
}
}
impl IntoResponse for String {
fn into_response(self) -> Response {
let mut res = Response::new(self.into());
let headers = res.headers_mut();
headers.typed_insert(ContentType::html());
res
}
}
impl IntoResponse for StatusCode {
fn into_response(self) -> Response {
let mut res = Response::new(Full::new(Bytes::new()));
*res.status_mut() = self;
res
}
}
impl IntoResponse for () {
fn into_response(self) -> Response {
Response::new(Full::new(Bytes::new()))
}
}
impl IntoResponse for http::Error {
fn into_response(self) -> Response {
StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
}
impl IntoResponse for serde_json::Error {
fn into_response(self) -> Response {
StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
}
impl<T> IntoResponse for Json<T>
where
T: Serialize,
{
fn into_response(self) -> Response {
match serde_json::to_vec(&self.0) {
Ok(data) => {
let mut res = Response::new(Full::from(data));
let headers = res.headers_mut();
headers.typed_insert(ContentType::json());
res
}
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response(),
}
}
fn into_json_response(self) -> Response
where
Self: Sized,
{
self.into_response()
}
fn into_html_response(self) -> Response
where
Self: Sized,
{
#[cfg(feature = "tracing")]
tracing::warn!("tried to return JSON as HTML response");
self.into_response()
}
}
impl<T, E> IntoResponse for Result<T, E>
where
T: IntoResponse,
E: IntoResponse,
{
fn into_response(self) -> Response {
match self {
Ok(ok) => ok.into_response(),
Err(err) => err.into_response(),
}
}
}
impl IntoResponse for ParseIntError {
fn into_response(self) -> Response {
StatusCode::BAD_REQUEST.into_response()
}
}
impl<B> IntoResponse for (StatusCode, B)
where
B: Into<Full<Bytes>>,
{
fn into_response(self) -> Response {
let mut res = Response::new(self.1.into());
*res.status_mut() = self.0;
let headers = res.headers_mut();
headers.typed_insert(ContentType::text_utf8());
res
}
}
impl IntoResponse for Full<Bytes> {
fn into_response(self) -> Response {
Response::new(self)
}
}
impl<const N: usize> IntoResponse for ([(HeaderName, HeaderValue); N], &'static str) {
fn into_response(self) -> Response {
let mut res = Response::new(self.1.into());
let headers = res.headers_mut();
for (key, val) in self.0 {
headers.insert(key, val);
}
res
}
}
impl<const N: usize> IntoResponse for ([(HeaderName, HeaderValue); N], String) {
fn into_response(self) -> Response {
let mut res = Response::new(self.1.into());
let headers = res.headers_mut();
for (key, val) in self.0 {
headers.insert(key, val);
}
res
}
}
impl<const H: usize, const B: usize> IntoResponse
for ([(HeaderName, HeaderValue); H], &'static [u8; B])
{
fn into_response(self) -> Response {
let mut res = Response::new(self.1.as_ref().into());
let headers = res.headers_mut();
for (key, val) in self.0 {
headers.insert(key, val);
}
res
}
}
#[cfg(feature = "either")]
impl<L, R> IntoResponse for either::Either<L, R>
where
L: IntoResponse,
R: IntoResponse,
{
fn into_response(self) -> Response {
match self {
either::Either::Left(l) => l.into_response(),
either::Either::Right(r) => r.into_response(),
}
}
}