2021-02-27 15:40:34 +00:00
|
|
|
use super::IntoResponse;
|
2020-05-01 14:48:11 +00:00
|
|
|
#[cfg(feature = "openapi")]
|
2021-02-27 15:40:34 +00:00
|
|
|
use crate::{OpenapiSchema, ResponseSchema};
|
2020-09-15 15:10:41 +02:00
|
|
|
use crate::{Response, ResponseBody};
|
2021-02-27 15:40:34 +00:00
|
|
|
use futures_util::future::{self, FutureExt};
|
|
|
|
use gotham::hyper::{
|
|
|
|
header::{HeaderMap, HeaderValue, IntoHeaderName},
|
|
|
|
StatusCode
|
2020-05-01 14:48:11 +00:00
|
|
|
};
|
2021-02-27 15:40:34 +00:00
|
|
|
use mime::{Mime, APPLICATION_JSON};
|
|
|
|
use std::{fmt::Debug, future::Future, pin::Pin};
|
2020-05-01 14:48:11 +00:00
|
|
|
|
|
|
|
/**
|
2021-02-27 15:40:34 +00:00
|
|
|
This can be returned from a resource when there is no cause of an error.
|
2020-05-01 14:48:11 +00:00
|
|
|
|
|
|
|
Usage example:
|
|
|
|
|
|
|
|
```
|
|
|
|
# #[macro_use] extern crate gotham_restful_derive;
|
|
|
|
# mod doc_tests_are_broken {
|
|
|
|
# use gotham::state::State;
|
|
|
|
# use gotham_restful::*;
|
|
|
|
# use serde::{Deserialize, Serialize};
|
|
|
|
#
|
|
|
|
# #[derive(Resource)]
|
2020-05-04 21:34:20 +02:00
|
|
|
# #[resource(read_all)]
|
2020-05-01 14:48:11 +00:00
|
|
|
# struct MyResource;
|
|
|
|
#
|
|
|
|
#[derive(Deserialize, Serialize)]
|
2020-05-16 01:01:20 +02:00
|
|
|
# #[cfg_attr(feature = "openapi", derive(OpenapiType))]
|
2020-05-01 14:48:11 +00:00
|
|
|
struct MyResponse {
|
|
|
|
message: &'static str
|
|
|
|
}
|
|
|
|
|
2021-01-18 00:05:30 +00:00
|
|
|
#[read_all]
|
|
|
|
fn read_all() -> Success<MyResponse> {
|
2020-05-01 14:48:11 +00:00
|
|
|
let res = MyResponse { message: "I'm always happy" };
|
|
|
|
res.into()
|
|
|
|
}
|
|
|
|
# }
|
|
|
|
```
|
|
|
|
*/
|
2021-02-27 15:40:34 +00:00
|
|
|
#[derive(Clone, Debug, Default)]
|
|
|
|
pub struct Success<T> {
|
|
|
|
value: T,
|
|
|
|
headers: HeaderMap
|
2020-05-01 14:48:11 +00:00
|
|
|
}
|
|
|
|
|
2020-09-15 15:10:41 +02:00
|
|
|
impl<T> From<T> for Success<T> {
|
|
|
|
fn from(t: T) -> Self {
|
2021-02-27 15:40:34 +00:00
|
|
|
Self {
|
|
|
|
value: t,
|
|
|
|
headers: HeaderMap::new()
|
|
|
|
}
|
2020-05-01 14:48:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-27 15:40:34 +00:00
|
|
|
impl<T> Success<T> {
|
|
|
|
/// Set a custom HTTP header. If a header with this name was set before, its value is being updated.
|
|
|
|
pub fn header<K: IntoHeaderName>(&mut self, name: K, value: HeaderValue) {
|
|
|
|
self.headers.insert(name, value);
|
2020-05-01 14:48:11 +00:00
|
|
|
}
|
|
|
|
|
2021-02-27 15:40:34 +00:00
|
|
|
/// Allow manipulating HTTP headers.
|
|
|
|
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
|
|
|
&mut self.headers
|
2020-05-01 14:48:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-27 15:40:34 +00:00
|
|
|
impl<T: ResponseBody> IntoResponse for Success<T> {
|
2020-05-01 14:48:11 +00:00
|
|
|
type Err = serde_json::Error;
|
2020-09-15 15:10:41 +02:00
|
|
|
|
|
|
|
fn into_response(self) -> Pin<Box<dyn Future<Output = Result<Response, Self::Err>> + Send>> {
|
2021-02-27 15:40:34 +00:00
|
|
|
let res =
|
|
|
|
serde_json::to_string(&self.value).map(|body| Response::json(StatusCode::OK, body).with_headers(self.headers));
|
|
|
|
future::ready(res).boxed()
|
2020-05-01 14:48:11 +00:00
|
|
|
}
|
2020-09-15 15:10:41 +02:00
|
|
|
|
|
|
|
fn accepted_types() -> Option<Vec<Mime>> {
|
2020-05-01 14:48:11 +00:00
|
|
|
Some(vec![APPLICATION_JSON])
|
|
|
|
}
|
2021-02-21 18:21:09 +00:00
|
|
|
}
|
2020-09-15 15:10:41 +02:00
|
|
|
|
2021-02-21 18:21:09 +00:00
|
|
|
#[cfg(feature = "openapi")]
|
2021-02-27 15:40:34 +00:00
|
|
|
impl<T: ResponseBody> ResponseSchema for Success<T> {
|
2020-09-15 15:10:41 +02:00
|
|
|
fn schema() -> OpenapiSchema {
|
2020-05-01 14:48:11 +00:00
|
|
|
T::schema()
|
|
|
|
}
|
|
|
|
}
|
2020-05-03 18:48:55 +02:00
|
|
|
|
|
|
|
#[cfg(test)]
|
2020-09-15 15:10:41 +02:00
|
|
|
mod test {
|
2020-05-03 18:48:55 +02:00
|
|
|
use super::*;
|
2021-02-27 15:40:34 +00:00
|
|
|
use crate::response::OrAllTypes;
|
2020-05-03 18:48:55 +02:00
|
|
|
use futures_executor::block_on;
|
2021-02-27 15:40:34 +00:00
|
|
|
use gotham::hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN;
|
2020-09-15 15:10:41 +02:00
|
|
|
|
2020-05-03 18:48:55 +02:00
|
|
|
#[derive(Debug, Default, Serialize)]
|
|
|
|
#[cfg_attr(feature = "openapi", derive(crate::OpenapiType))]
|
2020-09-15 15:10:41 +02:00
|
|
|
struct Msg {
|
|
|
|
msg: String
|
2020-05-03 18:48:55 +02:00
|
|
|
}
|
2020-09-15 15:10:41 +02:00
|
|
|
|
2020-05-03 18:48:55 +02:00
|
|
|
#[test]
|
2020-09-15 15:10:41 +02:00
|
|
|
fn success_always_successfull() {
|
|
|
|
let success: Success<Msg> = Msg::default().into();
|
2020-05-03 18:48:55 +02:00
|
|
|
let res = block_on(success.into_response()).expect("didn't expect error response");
|
|
|
|
assert_eq!(res.status, StatusCode::OK);
|
|
|
|
assert_eq!(res.mime, Some(APPLICATION_JSON));
|
2021-01-01 18:03:31 +01:00
|
|
|
assert_eq!(res.full_body().unwrap(), br#"{"msg":""}"#);
|
2021-02-27 15:40:34 +00:00
|
|
|
#[cfg(feature = "openapi")]
|
|
|
|
assert_eq!(<Success<Msg>>::default_status(), StatusCode::OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn success_custom_headers() {
|
|
|
|
let mut success: Success<Msg> = Msg::default().into();
|
|
|
|
success.header(ACCESS_CONTROL_ALLOW_ORIGIN, HeaderValue::from_static("*"));
|
|
|
|
let res = block_on(success.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("*"));
|
2020-05-03 18:48:55 +02:00
|
|
|
}
|
2020-09-15 15:10:41 +02:00
|
|
|
|
2020-05-03 18:48:55 +02:00
|
|
|
#[test]
|
2020-09-15 15:10:41 +02:00
|
|
|
fn success_accepts_json() {
|
2020-05-03 18:48:55 +02:00
|
|
|
assert!(<Success<Msg>>::accepted_types().or_all_types().contains(&APPLICATION_JSON))
|
|
|
|
}
|
|
|
|
}
|