1
//! To generate HTTP error response.
2
//!
3
//! ```
4
//! use sylvia_iot_corelib::err::ErrResp;
5
//! // To generate HTTP request body format error.
6
//! if format_error(body) {
7
//!     return Err(ErrResp::ErrParam(Some("input format error".to_string())));
8
//! }
9
//! ```
10

            
11
use std::{error::Error, fmt};
12

            
13
use axum::{
14
    Json,
15
    http::StatusCode,
16
    response::{IntoResponse, Response},
17
};
18
use serde::Serialize;
19
use serde_json;
20

            
21
/// The standard error definitions.
22
#[derive(Debug)]
23
pub enum ErrResp {
24
    ErrAuth(Option<String>),
25
    ErrDb(Option<String>),
26
    ErrIntMsg(Option<String>),
27
    ErrNotFound(Option<String>),
28
    ErrParam(Option<String>),
29
    ErrPerm(Option<String>),
30
    ErrRsc(Option<String>),
31
    ErrUnknown(Option<String>),
32
    Custom(u16, &'static str, Option<String>),
33
}
34

            
35
/// Used for generating HTTP body for errors.
36
#[derive(Serialize)]
37
struct RespJson<'a> {
38
    code: &'a str,
39
    #[serde(skip_serializing_if = "Option::is_none")]
40
    message: Option<&'a str>,
41
}
42

            
43
/// 401, token not authorized.
44
pub const E_AUTH: &'static str = "err_auth";
45
/// 503, database error.
46
pub const E_DB: &'static str = "err_db";
47
/// 503, internal service communication error.
48
pub const E_INT_MSG: &'static str = "err_int_msg";
49
/// 404, resource (in path) not found.
50
pub const E_NOT_FOUND: &'static str = "err_not_found";
51
/// 400, request (body) format error.
52
pub const E_PARAM: &'static str = "err_param";
53
/// 403, invalid permission.
54
pub const E_PERM: &'static str = "err_perm";
55
/// 503, allocate resource error.
56
pub const E_RSC: &'static str = "err_rsc";
57
/// 500, unknown error.
58
pub const E_UNKNOWN: &'static str = "err_unknown";
59

            
60
/// To generate error JSON string for HTTP body.
61
4
pub fn to_json(code: &str, message: Option<&str>) -> String {
62
4
    serde_json::to_string(&RespJson { code, message }).unwrap()
63
4
}
64

            
65
impl ErrResp {
66
60
    fn resp_json(&'_ self) -> RespJson<'_> {
67
60
        match *self {
68
6
            ErrResp::ErrAuth(ref desc) => RespJson {
69
6
                code: E_AUTH,
70
6
                message: desc.as_deref(),
71
6
            },
72
6
            ErrResp::ErrDb(ref desc) => RespJson {
73
6
                code: E_DB,
74
6
                message: desc.as_deref(),
75
6
            },
76
6
            ErrResp::ErrIntMsg(ref desc) => RespJson {
77
6
                code: E_INT_MSG,
78
6
                message: desc.as_deref(),
79
6
            },
80
6
            ErrResp::ErrNotFound(ref desc) => RespJson {
81
6
                code: E_NOT_FOUND,
82
6
                message: desc.as_deref(),
83
6
            },
84
12
            ErrResp::ErrParam(ref desc) => RespJson {
85
12
                code: E_PARAM,
86
12
                message: desc.as_deref(),
87
12
            },
88
6
            ErrResp::ErrPerm(ref desc) => RespJson {
89
6
                code: E_PERM,
90
6
                message: desc.as_deref(),
91
6
            },
92
6
            ErrResp::ErrRsc(ref desc) => RespJson {
93
6
                code: E_RSC,
94
6
                message: desc.as_deref(),
95
6
            },
96
6
            ErrResp::ErrUnknown(ref desc) => RespJson {
97
6
                code: E_UNKNOWN,
98
6
                message: desc.as_deref(),
99
6
            },
100
6
            ErrResp::Custom(_, err_code, ref desc) => RespJson {
101
6
                code: err_code,
102
6
                message: desc.as_deref(),
103
6
            },
104
        }
105
60
    }
106
}
107

            
108
impl fmt::Display for ErrResp {
109
36
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110
36
        write!(f, "{}", serde_json::to_string(&self.resp_json()).unwrap())
111
36
    }
112
}
113

            
114
impl Error for ErrResp {}
115

            
116
impl IntoResponse for ErrResp {
117
24
    fn into_response(self) -> Response {
118
24
        match self {
119
2
            ErrResp::ErrAuth(_) => (StatusCode::UNAUTHORIZED, Json(self.resp_json())),
120
2
            ErrResp::ErrDb(_) => (StatusCode::SERVICE_UNAVAILABLE, Json(self.resp_json())),
121
2
            ErrResp::ErrIntMsg(_) => (StatusCode::SERVICE_UNAVAILABLE, Json(self.resp_json())),
122
2
            ErrResp::ErrNotFound(_) => (StatusCode::NOT_FOUND, Json(self.resp_json())),
123
8
            ErrResp::ErrParam(_) => (StatusCode::BAD_REQUEST, Json(self.resp_json())),
124
2
            ErrResp::ErrPerm(_) => (StatusCode::FORBIDDEN, Json(self.resp_json())),
125
2
            ErrResp::ErrRsc(_) => (StatusCode::SERVICE_UNAVAILABLE, Json(self.resp_json())),
126
2
            ErrResp::ErrUnknown(_) => (StatusCode::INTERNAL_SERVER_ERROR, Json(self.resp_json())),
127
2
            ErrResp::Custom(code, _, _) => {
128
2
                (StatusCode::from_u16(code).unwrap(), Json(self.resp_json()))
129
            }
130
        }
131
24
        .into_response()
132
24
    }
133
}