use log::{debug,error,warn};
use serde::{Serialize,Deserialize,de::DeserializeOwned};
use hyper::{
header::{CONTENT_LENGTH,CONTENT_TYPE},
Response, Body,
StatusCode,
};
use serde_json::{self,Value};
#[derive(Debug,Serialize,Deserialize)]
pub struct BasicReply {
pub status: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub reply: Option<Value>,
}
impl BasicReply {
pub fn new(s: String, d: Option<Value>) -> BasicReply {
BasicReply {
status: s,
reply: d,
}
}
}
impl ApiReply for BasicReply {
fn error(status: String, data: Option<Value>) -> Self {
BasicReply::new(status,data)
}
}
pub trait ApiReply: Sized + Send + std::fmt::Debug + Serialize + DeserializeOwned {
fn error(status: String, data: Option<Value>) -> Self;
}
#[derive(Debug)]
pub enum JsonResponse<R: ApiReply = BasicReply> {
NotFound,
MethodNotAllowed,
BadRequest,
TooManyRequests,
InternalServerError(Option<Value>),
EmptyQuery,
RequestTimeout,
Locked,
ImATeapot,
MisdirectedRequest,
UnprocessableEntity,
Ok(R),
}
impl<R: ApiReply> JsonResponse<R> {
pub fn ok(value: R) -> JsonResponse<R> {
JsonResponse::Ok(value)
}
pub fn internal_error(er: &str) -> JsonResponse<R> {
match serde_json::to_value(er) {
Ok(v) => JsonResponse::InternalServerError(Some(v)),
Err(e) => {
error!("JsonResponse convert error: {:?}",e);
JsonResponse::InternalServerError(None)
}
}
}
pub fn empty_error() -> JsonResponse<R> {
JsonResponse::InternalServerError(None)
}
pub fn reply_value(self) -> R {
match self {
JsonResponse::NotFound => R::error("Not Found".to_string(),None),
JsonResponse::MethodNotAllowed => R::error("Method Not Allowed".to_string(),None),
JsonResponse::BadRequest => R::error("Bad Request".to_string(),None),
JsonResponse::TooManyRequests => R::error("Too Many Requests".to_string(),None),
JsonResponse::EmptyQuery => R::error("Empty Query".to_string(),None),
JsonResponse::RequestTimeout => R::error("Request Timeout".to_string(),None),
JsonResponse::Locked => R::error("Locked".to_string(),None),
JsonResponse::ImATeapot => R::error("I'm a teapot".to_string(),None),
JsonResponse::MisdirectedRequest => R::error("Misdirected Request".to_string(),None),
JsonResponse::UnprocessableEntity => R::error("Unprocessable Entity".to_string(),None),
JsonResponse::InternalServerError(v) => R::error("Internal Server Error".to_string(),v),
JsonResponse::Ok(v) => v,
}
}
fn status(&self) -> StatusCode {
match *self {
JsonResponse::NotFound => StatusCode::NOT_FOUND,
JsonResponse::MethodNotAllowed => StatusCode::METHOD_NOT_ALLOWED,
JsonResponse::TooManyRequests => StatusCode::TOO_MANY_REQUESTS,
JsonResponse::BadRequest |
JsonResponse::EmptyQuery => StatusCode::BAD_REQUEST,
JsonResponse::RequestTimeout => StatusCode::REQUEST_TIMEOUT,
JsonResponse::InternalServerError(..) => StatusCode::INTERNAL_SERVER_ERROR,
JsonResponse::Locked => StatusCode::LOCKED,
JsonResponse::ImATeapot => StatusCode::IM_A_TEAPOT,
JsonResponse::MisdirectedRequest => StatusCode::MISDIRECTED_REQUEST,
JsonResponse::UnprocessableEntity => StatusCode::UNPROCESSABLE_ENTITY,
JsonResponse::Ok(..) => StatusCode::OK,
}
}
pub fn to_response(self) -> Response<Body> {
fn error<R: ApiReply>() -> Response<Body> {
let r = JsonResponse::<R>::InternalServerError(None).reply_value();
if let Ok(json) = serde_json::to_string(&r) {
if let Ok(r) = Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.header(CONTENT_TYPE,mime::APPLICATION_JSON.as_ref())
.header(CONTENT_LENGTH,json.len() as u64)
.body(Body::from(json)) {
return r;
}
}
warn!("Error generating response (json-error)");
let mut response = Response::new(Body::empty());
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
response
}
let status = self.status();
match serde_json::to_string(&self.reply_value()) {
Ok(json) => {
debug!("Size: {}KB",json.len()/1024);
Response::builder()
.status(status)
.header(CONTENT_TYPE,mime::APPLICATION_JSON.as_ref())
.header(CONTENT_LENGTH,json.len() as u64)
.body(Body::from(json))
.unwrap_or_else(|_| {
warn!("Error generating response (json)");
error::<R>()
})
},
Err(e) => {
error!("Json error: {:?}",e);
error::<R>()
},
}
}
}
impl<R: ApiReply> Into<Response<Body>> for JsonResponse<R> {
fn into(self) -> Response<Body> {
self.to_response()
}
}