#[cfg(feature = "pgsql")]
use diesel::result::{DatabaseErrorInformation, DatabaseErrorKind, QueryResult};
#[cfg(feature = "pgsql")]
use diesel::result::Error as ResultError;
use rocket::http::Status;
use rocket_contrib::json::Json;
#[cfg(feature = "serialization")]
use serde::Serialize;
pub type ApiJsonResponse<T> = Result<Json<T>, Status>;
pub trait ApiResponse<T> {
fn into_json_response(self) -> ApiJsonResponse<T>;
}
#[cfg(feature = "pgsql")]
impl<T> ApiResponse<T> for QueryResult<T> {
fn into_json_response(self) -> ApiJsonResponse<T> {
self
.map(|item| Json(item))
.map_err(|error| match error {
ResultError::NotFound => Status::NotFound,
ResultError::DatabaseError(kind, details) => {
build_database_error_response(kind, details)
}
_ => error.into_failure_response(),
})
}
}
impl<T> ApiResponse<T> for Json<T> {
fn into_json_response(self) -> ApiJsonResponse<T> {
Ok(self)
}
}
#[cfg(feature = "serialization")]
impl<T> ApiResponse<T> for T
where T: Serialize
{
fn into_json_response(self) -> ApiJsonResponse<T> {
Ok(Json(self))
}
}
#[cfg(feature = "pgsql")]
pub trait ApiDeleteResponse<T> {
fn into_delete_json_response(self) -> ApiJsonResponse<()>;
}
#[cfg(feature = "pgsql")]
impl ApiDeleteResponse<usize> for QueryResult<usize> {
fn into_delete_json_response(self) -> ApiJsonResponse<()> {
match self {
Ok(0) => Err(Status::NotFound),
Ok(1) => Ok(Json(())),
_ => Err(Status::BadRequest),
}
}
}
pub trait FailureResponse {
fn into_failure_response(self) -> Status;
}
#[cfg(feature = "pgsql")]
impl FailureResponse for ResultError {
fn into_failure_response(self) -> Status {
build_internal_server_error_response(&self.to_string())
}
}
#[deprecated(since="0.6.0", note="Use directly Status::NotFound instead.")]
pub fn build_not_found_response() -> Status {
Status::NotFound
}
#[deprecated(since="0.6.0", note="Use directly Status::BadRequest instead.")]
pub fn build_bad_request_response() -> Status {
Status::BadRequest
}
pub fn build_internal_server_error_response(message: &str) -> Status {
warn!(target:"api", "{}", message);
Status::InternalServerError
}
#[cfg(feature = "pgsql")]
fn build_database_error_response(error_kind: DatabaseErrorKind, info: Box<dyn DatabaseErrorInformation>) -> Status {
match error_kind {
DatabaseErrorKind::UniqueViolation => Status::Conflict,
DatabaseErrorKind::ForeignKeyViolation => Status::BadRequest,
_ => build_internal_server_error_response(info.message()),
}
}