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
    http::StatusCode,
15
    response::{IntoResponse, Response},
16
    Json,
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
2
pub fn to_json(code: &str, message: Option<&str>) -> String {
62
2
    serde_json::to_string(&RespJson { code, message }).unwrap()
63
2
}
64

            
65
impl ErrResp {
66
30
    fn resp_json(&self) -> RespJson {
67
30
        match *self {
68
3
            ErrResp::ErrAuth(ref desc) => RespJson {
69
3
                code: E_AUTH,
70
3
                message: match desc.as_ref() {
71
2
                    None => None,
72
1
                    Some(desc) => Some(desc.as_str()),
73
                },
74
            },
75
3
            ErrResp::ErrDb(ref desc) => RespJson {
76
3
                code: E_DB,
77
3
                message: match desc.as_ref() {
78
2
                    None => None,
79
1
                    Some(desc) => Some(desc.as_str()),
80
                },
81
            },
82
3
            ErrResp::ErrIntMsg(ref desc) => RespJson {
83
3
                code: E_INT_MSG,
84
3
                message: match desc.as_ref() {
85
2
                    None => None,
86
1
                    Some(desc) => Some(desc.as_str()),
87
                },
88
            },
89
3
            ErrResp::ErrNotFound(ref desc) => RespJson {
90
3
                code: E_NOT_FOUND,
91
3
                message: match desc.as_ref() {
92
2
                    None => None,
93
1
                    Some(desc) => Some(desc.as_str()),
94
                },
95
            },
96
6
            ErrResp::ErrParam(ref desc) => RespJson {
97
6
                code: E_PARAM,
98
6
                message: match desc.as_ref() {
99
2
                    None => None,
100
4
                    Some(desc) => Some(desc.as_str()),
101
                },
102
            },
103
3
            ErrResp::ErrPerm(ref desc) => RespJson {
104
3
                code: E_PERM,
105
3
                message: match desc.as_ref() {
106
2
                    None => None,
107
1
                    Some(desc) => Some(desc.as_str()),
108
                },
109
            },
110
3
            ErrResp::ErrRsc(ref desc) => RespJson {
111
3
                code: E_RSC,
112
3
                message: match desc.as_ref() {
113
2
                    None => None,
114
1
                    Some(desc) => Some(desc.as_str()),
115
                },
116
            },
117
3
            ErrResp::ErrUnknown(ref desc) => RespJson {
118
3
                code: E_UNKNOWN,
119
3
                message: match desc.as_ref() {
120
2
                    None => None,
121
1
                    Some(desc) => Some(desc.as_str()),
122
                },
123
            },
124
3
            ErrResp::Custom(_, err_code, ref desc) => RespJson {
125
3
                code: err_code,
126
3
                message: desc.as_deref(),
127
3
            },
128
        }
129
30
    }
130
}
131

            
132
impl fmt::Display for ErrResp {
133
18
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134
18
        write!(f, "{}", serde_json::to_string(&self.resp_json()).unwrap())
135
18
    }
136
}
137

            
138
impl Error for ErrResp {}
139

            
140
impl IntoResponse for ErrResp {
141
12
    fn into_response(self) -> Response {
142
12
        match self {
143
1
            ErrResp::ErrAuth(_) => (StatusCode::UNAUTHORIZED, Json(self.resp_json())),
144
1
            ErrResp::ErrDb(_) => (StatusCode::SERVICE_UNAVAILABLE, Json(self.resp_json())),
145
1
            ErrResp::ErrIntMsg(_) => (StatusCode::SERVICE_UNAVAILABLE, Json(self.resp_json())),
146
1
            ErrResp::ErrNotFound(_) => (StatusCode::NOT_FOUND, Json(self.resp_json())),
147
4
            ErrResp::ErrParam(_) => (StatusCode::BAD_REQUEST, Json(self.resp_json())),
148
1
            ErrResp::ErrPerm(_) => (StatusCode::FORBIDDEN, Json(self.resp_json())),
149
1
            ErrResp::ErrRsc(_) => (StatusCode::SERVICE_UNAVAILABLE, Json(self.resp_json())),
150
1
            ErrResp::ErrUnknown(_) => (StatusCode::INTERNAL_SERVER_ERROR, Json(self.resp_json())),
151
1
            ErrResp::Custom(code, _, _) => {
152
1
                (StatusCode::from_u16(code).unwrap(), Json(self.resp_json()))
153
            }
154
        }
155
12
        .into_response()
156
12
    }
157
}