use crate::extractor::Json;
use crate::handler::{Resp, RespBody};
use bytes::Bytes;
use futures::{Stream, StreamExt};
use http_body_util::{BodyExt, Full, StreamBody};
use hyper::HeaderMap;
use hyper::{Response, StatusCode, body::Frame};
use serde::Serialize;
pub trait IntoResponse {
fn into_response(self) -> Resp;
}
pub fn bytes_to_boxed(bytes: Bytes) -> RespBody {
Full::new(bytes).map_err(Into::into).boxed_unsync()
}
impl IntoResponse for String {
fn into_response(self) -> Resp {
Response::builder()
.status(200)
.header("content-type", "text/plain; charset=utf-8")
.body(bytes_to_boxed(Bytes::from(self)))
.unwrap()
}
}
impl IntoResponse for &'static str {
fn into_response(self) -> Resp {
Response::builder()
.status(200)
.header("content-type", "text/plain; charset=utf-8")
.body(bytes_to_boxed(Bytes::from(self)))
.unwrap()
}
}
impl<T: Serialize> IntoResponse for Json<T> {
fn into_response(self) -> Resp {
let body = serde_json::to_vec(&self.0).unwrap();
Response::builder()
.header("content-type", "application/json")
.body(bytes_to_boxed(Bytes::from(body)))
.unwrap()
}
}
pub struct Html(pub String);
impl IntoResponse for Html {
fn into_response(self) -> Resp {
Response::builder()
.header("content-type", "text/html; charset=utf-8")
.body(bytes_to_boxed(Bytes::from(self.0)))
.unwrap()
}
}
impl IntoResponse for Resp {
fn into_response(self) -> Resp {
self
}
}
pub struct SSE<T>(pub T);
impl<S, E> IntoResponse for SSE<S>
where
S: Stream<Item = Result<Bytes, E>> + Send + Sync + 'static,
E: std::fmt::Debug + Send + 'static,
{
fn into_response(self) -> Resp {
let body = BodyExt::boxed_unsync(StreamBody::new(self.0.map(|chunk| match chunk {
Ok(b) => Ok(Frame::data(b)),
Err(e) => {
tracing::error!("SSE stream error: {:?}", e);
Ok(Frame::data(Bytes::from_static(b"\n")))
}
})));
Response::builder()
.status(200)
.header("content-type", "text/event-stream")
.body(body)
.unwrap()
}
}
impl IntoResponse for anyhow::Error {
fn into_response(self) -> Resp {
use crate::error::AppError;
let app_error: AppError = self.into();
app_error.into_response()
}
}
impl<T, E> IntoResponse for Result<T, E>
where
T: IntoResponse,
E: IntoResponse,
{
fn into_response(self) -> Resp {
match self {
Ok(t) => t.into_response(),
Err(e) => e.into_response(),
}
}
}
impl IntoResponse for () {
fn into_response(self) -> Resp {
Response::builder()
.status(200)
.body(bytes_to_boxed(Bytes::new()))
.unwrap()
}
}
impl IntoResponse for StatusCode {
fn into_response(self) -> Resp {
Response::builder()
.status(self)
.body(bytes_to_boxed(Bytes::new()))
.unwrap()
}
}
impl<T> IntoResponse for (StatusCode, T)
where
T: IntoResponse,
{
fn into_response(self) -> Resp {
let body = self.1.into_response();
Response::builder()
.status(self.0)
.body(body.into_body())
.unwrap()
}
}
impl<T> IntoResponse for (HeaderMap, T)
where
T: IntoResponse,
{
fn into_response(self) -> Resp {
let mut response = self.1.into_response();
let h = response.headers_mut();
for (name, value) in self.0 {
if name.is_none() {
continue;
}
h.insert(name.unwrap().clone(), value);
}
response
}
}
impl<T> IntoResponse for (StatusCode, HeaderMap, T)
where
T: IntoResponse,
{
fn into_response(self) -> Resp {
let mut response = self.2.into_response();
let h = response.headers_mut();
for (name, value) in self.1 {
if name.is_none() {
continue;
}
h.insert(name.unwrap().clone(), value);
}
*response.status_mut() = self.0;
response
}
}
impl IntoResponse for Vec<u8> {
fn into_response(self) -> Resp {
Response::builder()
.header("content-type", "application/octet-stream")
.body(bytes_to_boxed(Bytes::from(self)))
.unwrap()
}
}
impl IntoResponse for serde_json::Value {
fn into_response(self) -> Resp {
let body = serde_json::to_vec(&self).unwrap();
Response::builder()
.header("content-type", "application/json")
.body(bytes_to_boxed(Bytes::from(body)))
.unwrap()
}
}