use super::{handle_error, IntoResponseError, ResourceResult}; use crate::{FromBody, RequestBody, ResourceType, Response}; #[cfg(feature = "openapi")] use crate::{OpenapiSchema, OpenapiType, ResourceResultSchema}; use futures_core::future::Future; use futures_util::{future, future::FutureExt}; use gotham::hyper::{ body::{Body, Bytes}, StatusCode }; use mime::Mime; #[cfg(feature = "openapi")] use openapiv3::{SchemaKind, StringFormat, StringType, Type, VariantOrUnknownOrEmpty}; use serde_json::error::Error as SerdeJsonError; use std::{convert::Infallible, fmt::Display, pin::Pin}; /** This type can be used both as a raw request body, as well as as a raw response. However, all types of request bodies are accepted by this type. It is therefore recommended to derive your own type from [RequestBody] and only use this when you need to return a raw response. This is a usage example that simply returns its body: ```rust,no_run # #[macro_use] extern crate gotham_restful_derive; # use gotham::router::builder::*; # use gotham_restful::*; #[derive(Resource)] #[resource(create)] struct ImageResource; #[create] fn create(body : Raw>) -> Raw> { body } # fn main() { # gotham::start("127.0.0.1:8080", build_simple_router(|route| { # route.resource::("img"); # })); # } ``` */ #[derive(Debug)] pub struct Raw { pub raw: T, pub mime: Mime } impl Raw { pub fn new(raw: T, mime: Mime) -> Self { Self { raw, mime } } } impl AsMut for Raw where T: AsMut { fn as_mut(&mut self) -> &mut U { self.raw.as_mut() } } impl AsRef for Raw where T: AsRef { fn as_ref(&self) -> &U { self.raw.as_ref() } } impl Clone for Raw { fn clone(&self) -> Self { Self { raw: self.raw.clone(), mime: self.mime.clone() } } } impl From<&'a [u8]>> FromBody for Raw { type Err = Infallible; fn from_body(body: Bytes, mime: Mime) -> Result { Ok(Self::new(body.as_ref().into(), mime)) } } impl RequestBody for Raw where Raw: FromBody + ResourceType {} #[cfg(feature = "openapi")] impl OpenapiType for Raw { fn schema() -> OpenapiSchema { OpenapiSchema::new(SchemaKind::Type(Type::String(StringType { format: VariantOrUnknownOrEmpty::Item(StringFormat::Binary), ..Default::default() }))) } } impl> ResourceResult for Raw where Self: Send { type Err = SerdeJsonError; // just for easier handling of `Result, E>` fn into_response(self) -> Pin> + Send>> { future::ok(Response::new(StatusCode::OK, self.raw, Some(self.mime))).boxed() } } #[cfg(feature = "openapi")] impl> ResourceResultSchema for Raw where Self: Send { fn schema() -> OpenapiSchema { ::schema() } } impl ResourceResult for Result, E> where Raw: ResourceResult, E: Display + IntoResponseError as ResourceResult>::Err> { type Err = E::Err; fn into_response(self) -> Pin> + Send>> { match self { Ok(raw) => raw.into_response(), Err(e) => handle_error(e) } } } #[cfg(feature = "openapi")] impl ResourceResultSchema for Result, E> where Raw: ResourceResult + ResourceResultSchema, E: Display + IntoResponseError as ResourceResult>::Err> { fn schema() -> OpenapiSchema { as ResourceResultSchema>::schema() } } #[cfg(test)] mod test { use super::*; use futures_executor::block_on; use mime::TEXT_PLAIN; #[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()); } }