1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
use super::{handle_error, IntoResponse, ResourceError};
#[cfg(feature = "openapi")]
use crate::ResponseSchema;
use crate::{Response, ResponseBody, Success};
#[cfg(feature = "openapi")]
use openapi_type::OpenapiSchema;

use futures_core::future::Future;
use gotham::hyper::StatusCode;
use mime::{Mime, APPLICATION_JSON};
use std::{error::Error, fmt::Display, pin::Pin};

pub trait IntoResponseError {
	type Err: Display + Send + 'static;

	fn into_response_error(self) -> Result<Response, Self::Err>;
}

impl<E: Error> IntoResponseError for E {
	type Err = serde_json::Error;

	fn into_response_error(self) -> Result<Response, Self::Err> {
		let err: ResourceError = self.into();
		Ok(Response::json(
			StatusCode::INTERNAL_SERVER_ERROR,
			serde_json::to_string(&err)?
		))
	}
}

impl<R, E> IntoResponse for Result<R, E>
where
	R: ResponseBody,
	E: Display + IntoResponseError<Err = serde_json::Error>
{
	type Err = E::Err;

	fn into_response(self) -> Pin<Box<dyn Future<Output = Result<Response, E::Err>> + Send>> {
		match self {
			Ok(r) => Success::from(r).into_response(),
			Err(e) => handle_error(e)
		}
	}

	fn accepted_types() -> Option<Vec<Mime>> {
		Some(vec![APPLICATION_JSON])
	}
}

#[cfg(feature = "openapi")]
impl<R, E> ResponseSchema for Result<R, E>
where
	R: ResponseBody,
	E: Display + IntoResponseError<Err = serde_json::Error>
{
	fn schema() -> OpenapiSchema {
		R::schema()
	}
}

#[cfg(test)]
mod test {
	use super::*;
	use crate::response::OrAllTypes;
	use futures_executor::block_on;
	use thiserror::Error;

	#[derive(Debug, Default, Deserialize, Serialize)]
	#[cfg_attr(feature = "openapi", derive(openapi_type::OpenapiType))]
	struct Msg {
		msg: String
	}

	#[derive(Debug, Default, Error)]
	#[error("An Error")]
	struct MsgError;

	#[test]
	fn result_ok() {
		let ok: Result<Msg, MsgError> = Ok(Msg::default());
		let res = block_on(ok.into_response()).expect("didn't expect error response");
		assert_eq!(res.status, StatusCode::OK);
		assert_eq!(res.mime, Some(APPLICATION_JSON));
		assert_eq!(res.full_body().unwrap(), br#"{"msg":""}"#);
	}

	#[test]
	fn result_err() {
		let err: Result<Msg, MsgError> = Err(MsgError::default());
		let res = block_on(err.into_response()).expect("didn't expect error response");
		assert_eq!(res.status, StatusCode::INTERNAL_SERVER_ERROR);
		assert_eq!(res.mime, Some(APPLICATION_JSON));
		assert_eq!(
			res.full_body().unwrap(),
			format!(r#"{{"error":true,"message":"{}"}}"#, MsgError::default()).as_bytes()
		);
	}

	#[test]
	fn success_accepts_json() {
		assert!(<Result<Msg, MsgError>>::accepted_types()
			.or_all_types()
			.contains(&APPLICATION_JSON))
	}
}