errors.rs 2.71 KB
Newer Older
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
1 2
use serde::Serialize;
use rocket::http::Status;
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
3
use rocket::request::{Request, Outcome};
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
4 5
use rocket::response::{self, Response, Responder};
use rocket_contrib::json::Json;
6
use crate::models::users::UserError;
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
7 8
use serde_json::Value;

Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
9 10

#[derive(Serialize, Debug)]
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
11
pub struct ErrorResponse {
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
12 13 14 15 16
    #[serde(with = "StatusDef")]
    #[serde(flatten)]
    pub status: Status,
    pub message: String,
    #[serde(skip_serializing_if = "Option::is_none")]
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
17
    pub details: Option<Value>
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
18 19 20 21 22 23 24 25 26 27
}

#[derive(Serialize)]
#[serde(remote = "Status")]
struct StatusDef {
    code: u16,
    #[serde(rename = "status")]
    reason: &'static str,
}

Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
28 29
impl ErrorResponse {
    pub fn new(status: Status, message: String) -> ErrorResponse {
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
30 31 32 33 34 35 36
        ErrorResponse {
            status,
            message,
            details: None,
        }
    }

Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
37
    pub fn with_details<T: Serialize> (self, details: T) -> ErrorResponse {
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
38
        ErrorResponse {
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
39
            details: serde_json::to_value(details).ok(),
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
40 41 42 43
            ..self
        }
    }

Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
44
    pub fn err<R>(self) -> Result<R, ErrorResponse> {
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
45 46 47 48
        Err(self)
    }
}

Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
49
impl<'r> Responder<'r, 'static> for ErrorResponse {
50
    fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
51 52 53 54
        let status = self.status;
        Response::build_from(Json(self).respond_to(req)?).status(status).ok()
    }
}
55

Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
56
impl From<UserError> for ErrorResponse {
57 58
    fn from(e: UserError) -> Self {
        match e {
59 60 61 62 63 64
            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()),
65 66 67 68 69 70
            UserError::DbError(e) => make_500(e),
            UserError::PasswordError(e) => make_500(e)
        }
    }
}

Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
71

72 73 74
impl<S> From<ErrorResponse> for Outcome<S, ErrorResponse> {
    fn from(e: ErrorResponse) -> Self {
        Outcome::Failure(e.into())
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
75 76 77
    }
}

78 79 80 81

impl From<ErrorResponse> for (Status, ErrorResponse) {
    fn from(e: ErrorResponse) -> Self {
        (e.status, e)
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
82 83
    }
}
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
84 85

pub fn make_500<E: std::fmt::Debug>(e: E) -> ErrorResponse {
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
86
    println!("Making 500 for Error: {:?}", e);
87 88
    ErrorResponse::new(Status::InternalServerError, "An unexpected error occured.".into())
}