use bytes::Bytes;
use http::header::CONTENT_TYPE;
use http_body_util::Empty;
use crate::http::StatusCode;
use crate::http::{HeaderMap, Version};
use super::body::raw::RawBody;
use super::body::TypedBody;
use super::ResponseBody;
pub struct Response {
inner: http::Response<ResponseBody>,
}
#[non_exhaustive]
#[derive(Debug)]
pub struct ResponseHead {
status: StatusCode,
version: Version,
headers: HeaderMap,
}
impl Response {
pub fn new(status_code: StatusCode) -> Self {
let inner = http::Response::new(ResponseBody::new(Empty::new()));
Self { inner }.set_status(status_code)
}
}
impl Response {
pub fn set_status(mut self, status: StatusCode) -> Self {
*self.inner.status_mut() = status;
self
}
pub fn set_version(mut self, version: Version) -> Self {
*self.inner.version_mut() = version;
self
}
pub fn append_header(
mut self,
key: crate::http::HeaderName,
value: crate::http::HeaderValue,
) -> Self {
self.inner.headers_mut().append(key, value);
self
}
pub fn insert_header(
mut self,
key: crate::http::HeaderName,
value: crate::http::HeaderValue,
) -> Self {
self.inner.headers_mut().insert(key, value);
self
}
pub fn set_typed_body<NewBody>(self, body: NewBody) -> Response
where
NewBody: TypedBody,
<<NewBody as TypedBody>::Body as RawBody>::Error:
Into<Box<dyn std::error::Error + Send + Sync>>,
{
let (mut head, _) = self.inner.into_parts();
head.headers.insert(CONTENT_TYPE, body.content_type());
http::Response::from_parts(head, ResponseBody::new(body.body())).into()
}
pub fn set_raw_body<NewBody>(self, body: NewBody) -> Response
where
NewBody: RawBody<Data = Bytes> + Send + 'static,
<NewBody as RawBody>::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
{
let (head, _) = self.inner.into_parts();
http::Response::from_parts(head, ResponseBody::new(body)).into()
}
pub fn body_mut(&mut self) -> &mut ResponseBody {
self.inner.body_mut()
}
pub fn headers_mut(&mut self) -> &mut crate::http::HeaderMap {
self.inner.headers_mut()
}
}
impl Response {
pub fn status(&self) -> StatusCode {
self.inner.status()
}
pub fn version(&self) -> crate::http::Version {
self.inner.version()
}
pub fn headers(&self) -> &crate::http::HeaderMap {
self.inner.headers()
}
pub fn body(&self) -> &ResponseBody {
self.inner.body()
}
}
impl Response {
pub fn into_parts(self) -> (ResponseHead, ResponseBody) {
let (head, body) = self.inner.into_parts();
(head.into(), body)
}
pub fn from_parts(head: ResponseHead, body: ResponseBody) -> Self {
Self {
inner: http::Response::from_parts(head.into(), body),
}
}
}
impl<Body> From<http::Response<Body>> for Response
where
Body: Send + RawBody<Data = Bytes> + 'static,
<Body as RawBody>::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
{
fn from(inner: http::Response<Body>) -> Self {
let (head, body) = inner.into_parts();
let inner = http::Response::from_parts(head, ResponseBody::new(body));
Self { inner }
}
}
impl From<Response> for http::Response<ResponseBody> {
fn from(res: Response) -> Self {
res.inner
}
}
impl From<ResponseHead> for http::response::Parts {
fn from(head: ResponseHead) -> Self {
let ResponseHead {
status,
version,
headers,
} = head;
let (mut parts, _) = http::response::Response::builder()
.body(Empty::<()>::new())
.unwrap()
.into_parts();
parts.status = status;
parts.version = version;
parts.headers = headers;
parts
}
}
impl From<http::response::Parts> for ResponseHead {
fn from(parts: http::response::Parts) -> Self {
let http::response::Parts {
status,
version,
headers,
..
} = parts;
Self {
status,
version,
headers,
}
}
}
macro_rules! shorthand {
($name:ident) => {
paste::paste! {
#[doc = "Start building a new [`Response`] with [`" $name "`](`StatusCode::" $name "`) as status code."]
pub fn [<$name:lower>]() -> Response {
Response::new(StatusCode::[<$name>])
}
}
};
}
impl Response {
pub fn continue_() -> Response {
Response::new(StatusCode::CONTINUE)
}
shorthand!(SWITCHING_PROTOCOLS);
shorthand!(PROCESSING);
shorthand!(OK);
shorthand!(CREATED);
shorthand!(ACCEPTED);
shorthand!(NON_AUTHORITATIVE_INFORMATION);
shorthand!(NO_CONTENT);
shorthand!(RESET_CONTENT);
shorthand!(PARTIAL_CONTENT);
shorthand!(MULTI_STATUS);
shorthand!(ALREADY_REPORTED);
shorthand!(MULTIPLE_CHOICES);
shorthand!(MOVED_PERMANENTLY);
shorthand!(FOUND);
shorthand!(SEE_OTHER);
shorthand!(NOT_MODIFIED);
shorthand!(USE_PROXY);
shorthand!(TEMPORARY_REDIRECT);
shorthand!(PERMANENT_REDIRECT);
shorthand!(BAD_REQUEST);
shorthand!(NOT_FOUND);
shorthand!(UNAUTHORIZED);
shorthand!(PAYMENT_REQUIRED);
shorthand!(FORBIDDEN);
shorthand!(METHOD_NOT_ALLOWED);
shorthand!(NOT_ACCEPTABLE);
shorthand!(PROXY_AUTHENTICATION_REQUIRED);
shorthand!(REQUEST_TIMEOUT);
shorthand!(CONFLICT);
shorthand!(GONE);
shorthand!(LENGTH_REQUIRED);
shorthand!(PRECONDITION_FAILED);
shorthand!(PRECONDITION_REQUIRED);
shorthand!(PAYLOAD_TOO_LARGE);
shorthand!(URI_TOO_LONG);
shorthand!(UNSUPPORTED_MEDIA_TYPE);
shorthand!(RANGE_NOT_SATISFIABLE);
shorthand!(EXPECTATION_FAILED);
shorthand!(UNPROCESSABLE_ENTITY);
shorthand!(TOO_MANY_REQUESTS);
shorthand!(REQUEST_HEADER_FIELDS_TOO_LARGE);
shorthand!(UNAVAILABLE_FOR_LEGAL_REASONS);
shorthand!(INTERNAL_SERVER_ERROR);
shorthand!(NOT_IMPLEMENTED);
shorthand!(BAD_GATEWAY);
shorthand!(SERVICE_UNAVAILABLE);
shorthand!(GATEWAY_TIMEOUT);
shorthand!(HTTP_VERSION_NOT_SUPPORTED);
shorthand!(VARIANT_ALSO_NEGOTIATES);
shorthand!(INSUFFICIENT_STORAGE);
shorthand!(LOOP_DETECTED);
}