errors.rs 2.84 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

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

Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
78
79
80
81
82
impl Into<(Status, ErrorResponse)> for ErrorResponse {
    fn into(self) -> (Status, ErrorResponse) {
        (self.status.clone(), self)
    }
}
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
83
84

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

pub fn make_500_tuple<E: std::fmt::Debug>(e: E) -> (Status, ErrorResponse) {
    make_500(e).into()
}