use super::{handle_error, IntoResponse, IntoResponseError};
use crate::{types::ResourceType, FromBody, RequestBody, Response};
#[cfg(feature = "openapi")]
use crate::{IntoResponseWithSchema, ResponseSchema};
use futures_core::future::Future;
use futures_util::{future, future::FutureExt};
use gotham::{
hyper::{
body::{Body, Bytes},
StatusCode
},
mime::Mime
};
#[cfg(feature = "openapi")]
use openapi_type::{OpenapiSchema, OpenapiType, Visitor};
use serde_json::error::Error as SerdeJsonError;
use std::{convert::Infallible, fmt::Debug, pin::Pin};
#[derive(Debug)]
pub struct Raw<T> {
pub raw: T,
pub mime: Mime
}
impl<T> Raw<T> {
pub fn new(raw: T, mime: Mime) -> Self {
Self { raw, mime }
}
}
impl<T, U> AsMut<U> for Raw<T>
where
T: AsMut<U>
{
fn as_mut(&mut self) -> &mut U {
self.raw.as_mut()
}
}
impl<T, U> AsRef<U> for Raw<T>
where
T: AsRef<U>
{
fn as_ref(&self) -> &U {
self.raw.as_ref()
}
}
impl<T: Clone> Clone for Raw<T> {
fn clone(&self) -> Self {
Self {
raw: self.raw.clone(),
mime: self.mime.clone()
}
}
}
impl<T: for<'a> From<&'a [u8]>> FromBody for Raw<T> {
type Err = Infallible;
fn from_body(body: Bytes, mime: Mime) -> Result<Self, Self::Err> {
Ok(Self::new(body.as_ref().into(), mime))
}
}
impl<T> RequestBody for Raw<T> where Raw<T>: FromBody + ResourceType {}
#[cfg(feature = "openapi")]
impl<T> OpenapiType for Raw<T> {
fn visit_type<V: Visitor>(visitor: &mut V) {
visitor.visit_binary()
}
}
impl<T: Into<Body>> IntoResponse for Raw<T>
where
Self: Send
{
type Err = SerdeJsonError; fn into_response(
self
) -> Pin<Box<dyn Future<Output = Result<Response, SerdeJsonError>> + Send>> {
future::ok(Response::new(StatusCode::OK, self.raw, Some(self.mime))).boxed()
}
}
#[cfg(feature = "openapi")]
impl<T: Into<Body>> ResponseSchema for Raw<T>
where
Self: Send
{
fn schema(code: StatusCode) -> OpenapiSchema {
assert_eq!(code, StatusCode::OK);
<Self as OpenapiType>::schema()
}
}
impl<T, E> IntoResponse for Result<Raw<T>, E>
where
Raw<T>: IntoResponse,
E: Debug + IntoResponseError<Err = <Raw<T> as IntoResponse>::Err>
{
type Err = E::Err;
fn into_response(self) -> Pin<Box<dyn Future<Output = Result<Response, E::Err>> + Send>> {
match self {
Ok(raw) => raw.into_response(),
Err(e) => handle_error(e)
}
}
}
#[cfg(feature = "openapi")]
impl<T, E> ResponseSchema for Result<Raw<T>, E>
where
Raw<T>: IntoResponseWithSchema,
E: Debug + IntoResponseError<Err = <Raw<T> as IntoResponse>::Err>
{
fn status_codes() -> Vec<StatusCode> {
let mut status_codes = E::status_codes();
status_codes.push(StatusCode::OK);
status_codes
}
fn schema(code: StatusCode) -> OpenapiSchema {
match code {
StatusCode::OK => <Raw<T> as ResponseSchema>::schema(StatusCode::OK),
code => E::schema(code)
}
}
}
#[cfg(test)]
mod test {
use super::*;
use futures_executor::block_on;
use gotham::mime::TEXT_PLAIN;
use thiserror::Error;
#[derive(Debug, Default, Error)]
#[error("An Error")]
struct MsgError;
#[test]
fn raw_response() {
let msg = "Test";
let raw = Raw::new(msg, TEXT_PLAIN);
let res = block_on(raw.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::OK);
assert_eq!(res.mime, Some(TEXT_PLAIN));
assert_eq!(res.full_body().unwrap(), msg.as_bytes());
#[cfg(feature = "openapi")]
assert_eq!(<Raw<String>>::status_codes(), vec![StatusCode::OK]);
}
#[test]
fn raw_result() {
let msg = "Test";
let raw: Result<Raw<_>, MsgError> = Ok(Raw::new(msg, TEXT_PLAIN));
let res = block_on(raw.into_response()).expect("didn't expect error response");
assert_eq!(res.status, StatusCode::OK);
assert_eq!(res.mime, Some(TEXT_PLAIN));
assert_eq!(res.full_body().unwrap(), msg.as_bytes());
#[cfg(feature = "openapi")]
assert_eq!(<Result<Raw<String>, MsgError>>::status_codes(), vec![
StatusCode::INTERNAL_SERVER_ERROR,
StatusCode::OK
]);
}
}