use bytes::Bytes;
use http::header::{HeaderValue, CONTENT_TYPE};
use http::StatusCode;
use crate::body::RespBody;
pub mod json;
pub use json::{json_response, Json};
pub type Response = http::Response<RespBody>;
pub trait IntoResponse {
fn into_response(self) -> Response;
}
pub(crate) fn with_body(status: StatusCode, content_type: &'static str, body: Bytes) -> Response {
let mut response = http::Response::new(RespBody::new(body));
*response.status_mut() = status;
response
.headers_mut()
.insert(CONTENT_TYPE, HeaderValue::from_static(content_type));
response
}
pub fn bytes_response(status: StatusCode, content_type: &'static str, body: Bytes) -> Response {
with_body(status, content_type, body)
}
pub(crate) async fn into_body_bytes(response: Response) -> (http::response::Parts, Bytes) {
use http_body_util::BodyExt;
let (parts, body) = response.into_parts();
let bytes = body
.collect()
.await
.map(|collected| collected.to_bytes())
.unwrap_or_default();
(parts, bytes)
}
pub(crate) fn empty(status: StatusCode) -> Response {
let mut response = http::Response::new(RespBody::new(Bytes::new()));
*response.status_mut() = status;
response
}
impl IntoResponse for Response {
fn into_response(self) -> Response {
self
}
}
impl IntoResponse for StatusCode {
fn into_response(self) -> Response {
empty(self)
}
}
impl IntoResponse for () {
fn into_response(self) -> Response {
empty(StatusCode::OK)
}
}
impl<T, E> IntoResponse for core::result::Result<T, E>
where
T: IntoResponse,
E: Into<crate::error::Error>,
{
fn into_response(self) -> Response {
match self {
Ok(value) => value.into_response(),
Err(error) => error.into().into_response(),
}
}
}
#[doc(hidden)]
pub fn __finish<T, E>(
result: core::result::Result<T, E>,
status: StatusCode,
) -> crate::error::Result<Response>
where
T: serde::Serialize,
E: Into<crate::error::Error>,
{
match result {
Ok(value) => Ok(json::json_response(status, &value)),
Err(error) => Err(error.into()),
}
}
#[doc(hidden)]
pub fn __finish_into<T, U, E>(
result: core::result::Result<T, E>,
status: StatusCode,
) -> crate::error::Result<Response>
where
U: From<T> + serde::Serialize,
E: Into<crate::error::Error>,
{
match result {
Ok(value) => Ok(json::json_response(status, &U::from(value))),
Err(error) => Err(error.into()),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Error;
use bytes::Bytes;
use serde::Serialize;
#[derive(Serialize)]
struct Payload {
id: i64,
}
#[derive(Serialize)]
struct RawPayload {
id: i64,
}
impl From<RawPayload> for Payload {
fn from(value: RawPayload) -> Self {
Self { id: value.id }
}
}
#[tokio::test]
async fn status_code_and_unit_into_response_are_empty() {
let status = StatusCode::NO_CONTENT.into_response();
let unit = ().into_response();
let (status_parts, status_body) = into_body_bytes(status).await;
let (unit_parts, unit_body) = into_body_bytes(unit).await;
assert_eq!(status_parts.status, StatusCode::NO_CONTENT);
assert_eq!(status_body, Bytes::new());
assert_eq!(unit_parts.status, StatusCode::OK);
assert_eq!(unit_body, Bytes::new());
}
#[tokio::test]
async fn finish_helpers_serialize_success_values() {
let direct = __finish::<_, Error>(Ok(Payload { id: 7 }), StatusCode::CREATED).unwrap();
let mapped =
__finish_into::<_, Payload, Error>(Ok(RawPayload { id: 9 }), StatusCode::ACCEPTED)
.unwrap();
let (direct_parts, direct_body) = into_body_bytes(direct).await;
let (mapped_parts, mapped_body) = into_body_bytes(mapped).await;
assert_eq!(direct_parts.status, StatusCode::CREATED);
assert_eq!(mapped_parts.status, StatusCode::ACCEPTED);
assert_eq!(direct_body, Bytes::from_static(br#"{"id":7}"#));
assert_eq!(mapped_body, Bytes::from_static(br#"{"id":9}"#));
}
#[test]
fn result_into_response_and_finish_helpers_propagate_errors() {
let error = Error::bad_request("bad");
let response =
core::result::Result::<StatusCode, _>::Err(Error::bad_request("bad")).into_response();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
let finish = __finish::<Payload, _>(Err(error), StatusCode::OK)
.err()
.expect("expected handler error");
assert_eq!(finish.message(), "bad");
}
}