use super::{handle_error, IntoResponse};
#[cfg(feature = "openapi")]
use crate::ResponseSchema;
use crate::{IntoResponseError, Response};
use futures_util::{future, future::FutureExt};
#[cfg(feature = "openapi")]
use gotham::hyper::StatusCode;
use gotham::{
hyper::header::{HeaderMap, HeaderValue, IntoHeaderName},
mime::Mime
};
#[cfg(feature = "openapi")]
use openapi_type::{OpenapiSchema, OpenapiType};
use std::{fmt::Debug, future::Future, pin::Pin};
#[derive(Clone, Debug, Default)]
pub struct NoContent {
headers: HeaderMap
}
impl From<()> for NoContent {
fn from(_: ()) -> Self {
Self::default()
}
}
impl NoContent {
pub fn header<K: IntoHeaderName>(&mut self, name: K, value: HeaderValue) {
self.headers.insert(name, value);
}
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.headers
}
}
impl IntoResponse for NoContent {
type Err = serde_json::Error; fn into_response(self) -> Pin<Box<dyn Future<Output = Result<Response, Self::Err>> + Send>> {
future::ok(Response::no_content().with_headers(self.headers)).boxed()
}
fn accepted_types() -> Option<Vec<Mime>> {
Some(Vec::new())
}
}
#[cfg(feature = "openapi")]
impl ResponseSchema for NoContent {
fn status_codes() -> Vec<StatusCode> {
vec![StatusCode::NO_CONTENT]
}
fn schema(code: StatusCode) -> OpenapiSchema {
assert_eq!(code, StatusCode::NO_CONTENT);
<()>::schema()
}
}
impl<E> IntoResponse for Result<NoContent, E>
where
E: Debug + IntoResponseError<Err = serde_json::Error>
{
type Err = serde_json::Error;
fn into_response(
self
) -> Pin<Box<dyn Future<Output = Result<Response, serde_json::Error>> + Send>> {
match self {
Ok(nc) => nc.into_response(),
Err(e) => handle_error(e)
}
}
fn accepted_types() -> Option<Vec<Mime>> {
NoContent::accepted_types()
}
}
#[cfg(feature = "openapi")]
impl<E> ResponseSchema for Result<NoContent, E>
where
E: Debug + IntoResponseError<Err = serde_json::Error>
{
fn status_codes() -> Vec<StatusCode> {
let mut status_codes = E::status_codes();
status_codes.push(StatusCode::NO_CONTENT);
status_codes
}
fn schema(code: StatusCode) -> OpenapiSchema {
match code {
StatusCode::NO_CONTENT => <NoContent as ResponseSchema>::schema(StatusCode::NO_CONTENT),
code => E::schema(code)
}
}
}
#[cfg(test)]
mod test {
use super::*;
use futures_executor::block_on;
use gotham::hyper::{header::ACCESS_CONTROL_ALLOW_ORIGIN, StatusCode};
use thiserror::Error;
#[derive(Debug, Default, Error)]
#[error("An Error")]
struct MsgError;
#[test]
fn no_content_has_empty_response() {
let no_content = NoContent::default();
let res = block_on(no_content.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::NO_CONTENT);
assert_eq!(res.mime, None);
assert_eq!(res.full_body().unwrap(), &[] as &[u8]);
#[cfg(feature = "openapi")]
assert_eq!(NoContent::status_codes(), vec![StatusCode::NO_CONTENT]);
}
#[test]
fn no_content_result() {
let no_content: Result<NoContent, MsgError> = Ok(NoContent::default());
let res = block_on(no_content.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::NO_CONTENT);
assert_eq!(res.mime, None);
assert_eq!(res.full_body().unwrap(), &[] as &[u8]);
#[cfg(feature = "openapi")]
assert_eq!(<Result<NoContent, MsgError>>::status_codes(), vec![
StatusCode::INTERNAL_SERVER_ERROR,
StatusCode::NO_CONTENT
]);
}
#[test]
fn no_content_custom_headers() {
let mut no_content = NoContent::default();
no_content.header(ACCESS_CONTROL_ALLOW_ORIGIN, HeaderValue::from_static("*"));
let res = block_on(no_content.into_response()).expect("didn't expect error response");
let cors = res.headers.get(ACCESS_CONTROL_ALLOW_ORIGIN);
assert_eq!(cors.map(|value| value.to_str().unwrap()), Some("*"));
}
}