tenebrous-sheets/src/errors.rs

100 lines
2.8 KiB
Rust

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 std::convert::Into;
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("invalid input")]
InvalidInput,
#[error("validation error: {0}")]
ValidationError(#[from] crate::models::convert::ValidationError),
#[error("serialization error: {0}")]
SerializationError(#[from] prost::EncodeError),
#[error("deserialization error: {0}")]
DeserializationError(#[from] prost::DecodeError),
#[error("i/o error: {0}")]
IoError(#[from] std::io::Error),
#[error("query error: {0}")]
QueryError(#[from] sqlx::Error),
#[error("rocket error: {0}")]
RocketError(#[from] rocket::error::Error),
}
impl Error {
fn is_sensitive(&self) -> bool {
use Error::*;
match self {
QueryError(_) => true,
IoError(_) => true,
_ => false,
}
}
fn message(&self) -> String {
if self.is_sensitive() {
"internal error".to_string()
} else {
self.to_string()
}
}
}
impl From<Error> for tonic::Status {
fn from(err: Error) -> tonic::Status {
use tonic::{Code, Status};
use Error::*;
match err {
NotFound => Status::new(Code::NotFound, err.message()),
NotLoggedIn => Status::new(Code::Unauthenticated, err.message()),
NoPermission => Status::new(Code::PermissionDenied, err.message()),
InvalidInput => Status::new(Code::InvalidArgument, err.message()),
_ => Status::new(Code::Internal, err.message()),
}
}
}
#[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),
}
}
}