sylvia_iot_corelib/
err.rs

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
11use std::{error::Error, fmt};
12
13use axum::{
14    http::StatusCode,
15    response::{IntoResponse, Response},
16    Json,
17};
18use serde::Serialize;
19use serde_json;
20
21/// The standard error definitions.
22#[derive(Debug)]
23pub 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)]
37struct 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.
44pub const E_AUTH: &'static str = "err_auth";
45/// 503, database error.
46pub const E_DB: &'static str = "err_db";
47/// 503, internal service communication error.
48pub const E_INT_MSG: &'static str = "err_int_msg";
49/// 404, resource (in path) not found.
50pub const E_NOT_FOUND: &'static str = "err_not_found";
51/// 400, request (body) format error.
52pub const E_PARAM: &'static str = "err_param";
53/// 403, invalid permission.
54pub const E_PERM: &'static str = "err_perm";
55/// 503, allocate resource error.
56pub const E_RSC: &'static str = "err_rsc";
57/// 500, unknown error.
58pub const E_UNKNOWN: &'static str = "err_unknown";
59
60/// To generate error JSON string for HTTP body.
61pub fn to_json(code: &str, message: Option<&str>) -> String {
62    serde_json::to_string(&RespJson { code, message }).unwrap()
63}
64
65impl ErrResp {
66    fn resp_json(&self) -> RespJson {
67        match *self {
68            ErrResp::ErrAuth(ref desc) => RespJson {
69                code: E_AUTH,
70                message: match desc.as_ref() {
71                    None => None,
72                    Some(desc) => Some(desc.as_str()),
73                },
74            },
75            ErrResp::ErrDb(ref desc) => RespJson {
76                code: E_DB,
77                message: match desc.as_ref() {
78                    None => None,
79                    Some(desc) => Some(desc.as_str()),
80                },
81            },
82            ErrResp::ErrIntMsg(ref desc) => RespJson {
83                code: E_INT_MSG,
84                message: match desc.as_ref() {
85                    None => None,
86                    Some(desc) => Some(desc.as_str()),
87                },
88            },
89            ErrResp::ErrNotFound(ref desc) => RespJson {
90                code: E_NOT_FOUND,
91                message: match desc.as_ref() {
92                    None => None,
93                    Some(desc) => Some(desc.as_str()),
94                },
95            },
96            ErrResp::ErrParam(ref desc) => RespJson {
97                code: E_PARAM,
98                message: match desc.as_ref() {
99                    None => None,
100                    Some(desc) => Some(desc.as_str()),
101                },
102            },
103            ErrResp::ErrPerm(ref desc) => RespJson {
104                code: E_PERM,
105                message: match desc.as_ref() {
106                    None => None,
107                    Some(desc) => Some(desc.as_str()),
108                },
109            },
110            ErrResp::ErrRsc(ref desc) => RespJson {
111                code: E_RSC,
112                message: match desc.as_ref() {
113                    None => None,
114                    Some(desc) => Some(desc.as_str()),
115                },
116            },
117            ErrResp::ErrUnknown(ref desc) => RespJson {
118                code: E_UNKNOWN,
119                message: match desc.as_ref() {
120                    None => None,
121                    Some(desc) => Some(desc.as_str()),
122                },
123            },
124            ErrResp::Custom(_, err_code, ref desc) => RespJson {
125                code: err_code,
126                message: desc.as_deref(),
127            },
128        }
129    }
130}
131
132impl fmt::Display for ErrResp {
133    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134        write!(f, "{}", serde_json::to_string(&self.resp_json()).unwrap())
135    }
136}
137
138impl Error for ErrResp {}
139
140impl IntoResponse for ErrResp {
141    fn into_response(self) -> Response {
142        match self {
143            ErrResp::ErrAuth(_) => (StatusCode::UNAUTHORIZED, Json(self.resp_json())),
144            ErrResp::ErrDb(_) => (StatusCode::SERVICE_UNAVAILABLE, Json(self.resp_json())),
145            ErrResp::ErrIntMsg(_) => (StatusCode::SERVICE_UNAVAILABLE, Json(self.resp_json())),
146            ErrResp::ErrNotFound(_) => (StatusCode::NOT_FOUND, Json(self.resp_json())),
147            ErrResp::ErrParam(_) => (StatusCode::BAD_REQUEST, Json(self.resp_json())),
148            ErrResp::ErrPerm(_) => (StatusCode::FORBIDDEN, Json(self.resp_json())),
149            ErrResp::ErrRsc(_) => (StatusCode::SERVICE_UNAVAILABLE, Json(self.resp_json())),
150            ErrResp::ErrUnknown(_) => (StatusCode::INTERNAL_SERVER_ERROR, Json(self.resp_json())),
151            ErrResp::Custom(code, _, _) => {
152                (StatusCode::from_u16(code).unwrap(), Json(self.resp_json()))
153            }
154        }
155        .into_response()
156    }
157}