use std::borrow::Cow;
use std::convert::TryInto;
use std::fmt::{self, Display};
use crate::header::{HeaderMap, HeaderName, HeaderValue};
use crate::proto::h1::ext::ReasonPhrase;
use crate::status::StatusCode;
use crate::version::Version;
use crate::{Body, Result};
use rama_core::extensions::{Extension, Extensions, ExtensionsRef};
use rama_utils::macros::generate_set_and_with;
#[derive(Clone)]
pub struct Response<T = Body> {
head: Parts,
body: T,
}
#[non_exhaustive]
#[derive(Clone)]
pub struct Parts {
pub status: StatusCode,
pub version: Version,
pub headers: HeaderMap<HeaderValue>,
pub extensions: Extensions,
}
impl ExtensionsRef for Parts {
fn extensions(&self) -> &Extensions {
&self.extensions
}
}
#[derive(Debug)]
#[must_use]
pub struct Builder {
inner: Result<Parts>,
}
impl Response<()> {
#[inline]
pub fn builder() -> Builder {
Builder::new()
}
#[inline]
pub fn builder_with_extensions(ext: Extensions) -> Builder {
Builder::new_with_extensions(ext)
}
}
impl<T> Response<T> {
#[inline]
pub fn new(body: T) -> Self {
Self {
head: Parts::new(),
body,
}
}
#[inline]
pub fn from_parts(parts: Parts, body: T) -> Self {
Self { head: parts, body }
}
#[inline]
pub fn status(&self) -> StatusCode {
self.head.status
}
#[inline]
pub fn status_mut(&mut self) -> &mut StatusCode {
&mut self.head.status
}
pub fn error_for_status(self) -> std::result::Result<Self, StatusCodeError> {
let status = self.status();
if status.is_client_error() || status.is_server_error() {
Err(StatusCodeError {
status,
reason: match self.extensions().get_ref::<ReasonPhrase>() {
Some(reason) => Some(
String::from_utf8_lossy(reason.as_bytes())
.into_owned()
.into(),
),
None => status.canonical_reason().map(Into::into),
},
})
} else {
Ok(self)
}
}
pub fn error_for_status_ref(&self) -> std::result::Result<&Self, StatusCodeError> {
let status = self.status();
if status.is_client_error() || status.is_server_error() {
Err(StatusCodeError {
status,
reason: match self.extensions().get_ref::<ReasonPhrase>() {
Some(reason) => Some(
String::from_utf8_lossy(reason.as_bytes())
.into_owned()
.into(),
),
None => status.canonical_reason().map(Into::into),
},
})
} else {
Ok(self)
}
}
#[inline]
pub fn version(&self) -> Version {
self.head.version
}
#[inline]
pub fn version_mut(&mut self) -> &mut Version {
&mut self.head.version
}
#[inline]
pub fn headers(&self) -> &HeaderMap<HeaderValue> {
&self.head.headers
}
#[inline]
pub fn headers_mut(&mut self) -> &mut HeaderMap<HeaderValue> {
&mut self.head.headers
}
#[inline]
pub fn clone_parts(&self) -> Parts {
self.head.clone()
}
#[inline]
pub fn body(&self) -> &T {
&self.body
}
#[inline]
pub fn body_mut(&mut self) -> &mut T {
&mut self.body
}
#[inline]
pub fn into_body(self) -> T {
self.body
}
#[inline]
pub fn into_parts(self) -> (Parts, T) {
(self.head, self.body)
}
#[inline]
pub fn map<F, U>(self, f: F) -> Response<U>
where
F: FnOnce(T) -> U,
{
Response {
body: f(self.body),
head: self.head,
}
}
generate_set_and_with! {
pub fn extensions(mut self, extensions: Extensions) -> Self {
self.head.extensions = extensions;
self
}
}
}
impl<T: Default> Default for Response<T> {
#[inline]
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: fmt::Debug> fmt::Debug for Response<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Response")
.field("status", &self.status())
.field("version", &self.version())
.field("headers", self.headers())
.field("body", self.body())
.finish()
}
}
impl<T> ExtensionsRef for Response<T> {
fn extensions(&self) -> &Extensions {
&self.head.extensions
}
}
#[derive(Debug)]
pub struct StatusCodeError {
status: StatusCode,
reason: Option<Cow<'static, str>>,
}
impl StatusCodeError {
pub fn status(&self) -> StatusCode {
self.status
}
pub fn reason(&self) -> Option<&str> {
self.reason.as_deref()
}
}
impl Display for StatusCodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.status.is_client_error() {
write!(
f,
"http client error: status={}; reason: '{}'",
self.status,
self.reason.as_deref().unwrap_or_default(),
)
} else if self.status.is_server_error() {
write!(
f,
"http server error: status={}; reason: '{}'",
self.status,
self.reason.as_deref().unwrap_or_default(),
)
} else {
write!(
f,
"http error: status={}; reason: '{}'",
self.status,
self.reason.as_deref().unwrap_or_default(),
)
}
}
}
impl std::error::Error for StatusCodeError {}
impl Parts {
fn new() -> Self {
Self {
status: StatusCode::default(),
version: Version::default(),
headers: HeaderMap::default(),
extensions: Extensions::default(),
}
}
}
impl fmt::Debug for Parts {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Parts")
.field("status", &self.status)
.field("version", &self.version)
.field("headers", &self.headers)
.finish()
}
}
impl Builder {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn new_with_extensions(ext: Extensions) -> Self {
Self {
inner: Ok(Parts {
status: StatusCode::default(),
version: Version::default(),
headers: HeaderMap::default(),
extensions: ext,
}),
}
}
pub fn status<T>(self, status: T) -> Self
where
T: TryInto<StatusCode>,
<T as TryInto<StatusCode>>::Error: Into<crate::Error>,
{
self.and_then(move |mut head| {
head.status = status.try_into().map_err(Into::into)?;
Ok(head)
})
}
pub fn version(self, version: Version) -> Self {
self.and_then(move |mut head| {
head.version = version;
Ok(head)
})
}
pub fn header<K, V>(self, key: K, value: V) -> Self
where
K: TryInto<HeaderName>,
<K as TryInto<HeaderName>>::Error: Into<crate::Error>,
V: TryInto<HeaderValue>,
<V as TryInto<HeaderValue>>::Error: Into<crate::Error>,
{
self.and_then(move |mut head| {
let name = key.try_into().map_err(Into::into)?;
let value = value.try_into().map_err(Into::into)?;
head.headers.try_append(name, value)?;
Ok(head)
})
}
#[must_use]
pub fn headers_ref(&self) -> Option<&HeaderMap<HeaderValue>> {
self.inner.as_ref().ok().map(|h| &h.headers)
}
pub fn headers_mut(&mut self) -> Option<&mut HeaderMap<HeaderValue>> {
self.inner.as_mut().ok().map(|h| &mut h.headers)
}
pub fn extension<T>(self, extension: T) -> Self
where
T: Extension,
{
self.and_then(move |head| {
head.extensions.insert(extension);
Ok(head)
})
}
#[must_use]
pub fn extensions(&self) -> Option<&Extensions> {
self.inner.as_ref().ok().map(|h| &h.extensions)
}
pub fn body<T>(self, body: T) -> Result<Response<T>> {
self.inner.map(move |head| Response { head, body })
}
fn and_then<F>(self, func: F) -> Self
where
F: FnOnce(Parts) -> Result<Parts>,
{
Self {
inner: self.inner.and_then(func),
}
}
}
impl Default for Builder {
#[inline]
fn default() -> Self {
Self {
inner: Ok(Parts::new()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_can_map_a_body_from_one_type_to_another() {
let response = Response::builder().body("some string").unwrap();
let mapped_response = response.map(|s| {
assert_eq!(s, "some string");
123u32
});
assert_eq!(mapped_response.body(), &123u32);
}
}