use rocket::http::Status; use rocket::request::Request; use rocket::response::status; use rocket::response::{self, Responder}; use rocket_contrib::templates::Template; use std::collections::HashMap; use thiserror::Error; #[derive(Error, Debug)] pub enum Error { #[error("resource not found")] NotFound, #[error("you must be logged in")] NotLoggedIn, #[error("you do not have permission to access this")] NoPermission, #[error("query error: {0}")] QueryError(#[from] diesel::result::Error), #[error("serialization error: {0}")] SerializationError(#[from] prost::EncodeError), #[error("deserialization error: {0}")] DeserializationError(#[from] prost::DecodeError), } impl Error { fn is_sensitive(&self) -> bool { use Error::*; match self { QueryError(_) => true, _ => false, } } } #[rocket::async_trait] impl<'r> Responder<'r, 'static> for Error { fn respond_to(self, req: &Request) -> response::Result<'static> { log::error!("error: {0}", self.to_string()); //Hide sensitive error information let message: String = if self.is_sensitive() { "internal error".into() } else { self.to_string() }; let mut context = HashMap::new(); context.insert("message", message); let resp = Template::render("error", context).respond_to(req)?; use Error::*; match self { NotFound => status::NotFound(resp).respond_to(req), NotLoggedIn => status::Forbidden(Some(resp)).respond_to(req), NoPermission => status::Forbidden(Some(resp)).respond_to(req), _ => status::Custom(Status::InternalServerError, resp).respond_to(req), } } }