use serde::Serialize; use rocket::http::Status; use rocket::request::{Request, Outcome}; use rocket::response::{self, Response, Responder}; use rocket_contrib::json::Json; use crate::models::users::UserError; use serde_json::Value; #[derive(Serialize, Debug)] pub struct ErrorResponse { #[serde(with = "StatusDef")] #[serde(flatten)] pub status: Status, pub message: String, #[serde(skip_serializing_if = "Option::is_none")] pub details: Option } #[derive(Serialize)] #[serde(remote = "Status")] struct StatusDef { code: u16, #[serde(rename = "status")] reason: &'static str, } impl ErrorResponse { pub fn new(status: Status, message: String) -> ErrorResponse { ErrorResponse { status, message, details: None, } } pub fn with_details (self, details: T) -> ErrorResponse { ErrorResponse { details: serde_json::to_value(details).ok(), ..self } } pub fn err(self) -> Result { Err(self) } } impl<'r> Responder<'r, 'static> for ErrorResponse { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { let status = self.status; Response::build_from(Json(self).respond_to(req)?).status(status).ok() } } impl From for ErrorResponse { fn from(e: UserError) -> Self { match e { UserError::NotFound => ErrorResponse::new(Status::Unauthorized, "Provided credentials or token do not match any existing user".into()), UserError::UserExists => ErrorResponse::new(Status::Conflict, "User already exists".into()), UserError::BadToken => ErrorResponse::new(Status::BadRequest, "Malformed token".into()), UserError::ExpiredToken => ErrorResponse::new(Status::Unauthorized, "The provided token has expired".into()), UserError::MalformedHeader => ErrorResponse::new(Status::BadRequest, "Malformed authorization header".into()), UserError::PermissionDenied => ErrorResponse::new(Status::Forbidden, "Bearer is not authorized to access the resource".into()), UserError::ZoneNotFound => ErrorResponse::new(Status::NotFound, "DNS zone does not exists".into()), UserError::DbError(e) => make_500(e), UserError::PasswordError(e) => make_500(e) } } } impl From for Outcome { fn from(e: ErrorResponse) -> Self { Outcome::Failure(e.into()) } } impl From for (Status, ErrorResponse) { fn from(e: ErrorResponse) -> Self { (e.status, e) } } pub fn make_500(e: E) -> ErrorResponse { println!("Making 500 for Error: {:?}", e); ErrorResponse::new(Status::InternalServerError, "An unexpected error occured.".into()) }