nanoservices_utils/
errors.rs1use serde::{Deserialize, Serialize};
5use bitcode::{Encode, Decode};
6use thiserror::Error;
7use std::fmt;
8use revision::revisioned;
9
10#[cfg(feature = "actix")]
11use actix_web::{
12 HttpResponse,
13 error::ResponseError,
14 http::StatusCode
15};
16
17#[cfg(feature = "rocket")]
18use rocket::{
19 http::Status,
20 response::{Responder, Response},
21 Request,
22};
23
24#[cfg(feature = "axum")]
25use axum::{
26 response::{IntoResponse, Response as AxumResponse},
27 http::StatusCode as AxumStatusCode,
28 Json
29};
30
31#[cfg(feature = "hyper")]
32use hyper::{
33 Response as HyperResponse,
34 body::Bytes,
35 StatusCode as HyperStatusCode,
36 header
37};
38#[cfg(feature = "hyper")]
39use http_body_util::Full;
40
41
42#[derive(Error, Debug, Serialize, Deserialize, PartialEq, Clone, Encode, Decode)]
43#[revisioned(revision = 1)]
44pub enum NanoServiceErrorStatus {
45 #[error("Requested resource was not found")]
46 NotFound,
47 #[error("You are forbidden to access requested resource.")]
48 Forbidden,
49 #[error("Unknown Internal Error")]
50 Unknown,
51 #[error("Bad Request")]
52 BadRequest,
53 #[error("Conflict")]
54 Conflict,
55 #[error("Unauthorized")]
56 Unauthorized,
57 #[error("Contract not supported")]
58 ContractNotSupported,
59}
60
61
62#[derive(Serialize, Deserialize, Debug, Error, PartialEq, Clone, Encode, Decode)]
68#[revisioned(revision = 1)]
69pub struct NanoServiceError {
70 pub message: String,
71 pub status: NanoServiceErrorStatus
72}
73
74impl NanoServiceError {
75
76 pub fn new(message: String, status: NanoServiceErrorStatus) -> NanoServiceError {
85 NanoServiceError {
86 message,
87 status
88 }
89 }
90}
91
92impl fmt::Display for NanoServiceError {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 write!(f, "{}", self.message)
95 }
96}
97
98
99#[cfg(feature = "actix")]
100impl ResponseError for NanoServiceError {
101
102 fn status_code(&self) -> StatusCode {
107 match self.status {
108 NanoServiceErrorStatus::NotFound =>
109 StatusCode::NOT_FOUND,
110 NanoServiceErrorStatus::Forbidden =>
111 StatusCode::FORBIDDEN,
112 NanoServiceErrorStatus::Unknown =>
113 StatusCode::INTERNAL_SERVER_ERROR,
114 NanoServiceErrorStatus::BadRequest =>
115 StatusCode::BAD_REQUEST,
116 NanoServiceErrorStatus::Conflict =>
117 StatusCode::CONFLICT,
118 NanoServiceErrorStatus::Unauthorized =>
119 StatusCode::UNAUTHORIZED,
120 NanoServiceErrorStatus::ContractNotSupported =>
121 StatusCode::NOT_IMPLEMENTED
122 }
123 }
124
125 fn error_response(&self) -> HttpResponse {
130 let status_code = self.status_code();
131 HttpResponse::build(status_code).json(self.message.clone())
132 }
133}
134
135
136#[cfg(feature = "rocket")]
137#[rocket::async_trait]
138impl<'r> Responder<'r, 'static> for NanoServiceError {
139 fn respond_to(self, _: &'r Request<'_>) -> rocket::response::Result<'static> {
140 let status = match self.status {
141 NanoServiceErrorStatus::NotFound => Status::NotFound,
142 NanoServiceErrorStatus::Forbidden => Status::Forbidden,
143 NanoServiceErrorStatus::Unknown => Status::InternalServerError,
144 NanoServiceErrorStatus::BadRequest => Status::BadRequest,
145 NanoServiceErrorStatus::Conflict => Status::Conflict,
146 NanoServiceErrorStatus::Unauthorized => Status::Unauthorized,
147 NanoServiceErrorStatus::ContractNotSupported => Status::NotImplemented
148 };
149
150 Response::build()
151 .status(status)
152 .sized_body(self.message.len(), std::io::Cursor::new(self.message))
153 .ok()
154 }
155}
156
157#[cfg(feature = "axum")]
159impl IntoResponse for NanoServiceError {
160 fn into_response(self) -> AxumResponse {
161 let status_code = match self.status {
162 NanoServiceErrorStatus::NotFound => AxumStatusCode::NOT_FOUND,
163 NanoServiceErrorStatus::Forbidden => AxumStatusCode::FORBIDDEN,
164 NanoServiceErrorStatus::Unknown => AxumStatusCode::INTERNAL_SERVER_ERROR,
165 NanoServiceErrorStatus::BadRequest => AxumStatusCode::BAD_REQUEST,
166 NanoServiceErrorStatus::Conflict => AxumStatusCode::CONFLICT,
167 NanoServiceErrorStatus::Unauthorized => AxumStatusCode::UNAUTHORIZED,
168 NanoServiceErrorStatus::ContractNotSupported => AxumStatusCode::NOT_IMPLEMENTED
169 };
170
171 (status_code, Json(self.message)).into_response()
172 }
173}
174
175#[cfg(feature = "hyper")]
176impl NanoServiceError {
177 pub fn into_hyper_response(self) -> HyperResponse<Full<Bytes>> {
178 let status_code = match self.status {
179 NanoServiceErrorStatus::NotFound => HyperStatusCode::NOT_FOUND,
180 NanoServiceErrorStatus::Forbidden => HyperStatusCode::FORBIDDEN,
181 NanoServiceErrorStatus::Unknown => HyperStatusCode::INTERNAL_SERVER_ERROR,
182 NanoServiceErrorStatus::BadRequest => HyperStatusCode::BAD_REQUEST,
183 NanoServiceErrorStatus::Conflict => HyperStatusCode::CONFLICT,
184 NanoServiceErrorStatus::Unauthorized => HyperStatusCode::UNAUTHORIZED,
185 NanoServiceErrorStatus::ContractNotSupported => HyperStatusCode::NOT_IMPLEMENTED
186 };
187
188 let json_body = serde_json::to_string(&self.message).unwrap();
189
190 HyperResponse::builder()
191 .header(header::CONTENT_TYPE, "application/json")
192 .status(status_code)
193 .body(Full::new(Bytes::from(json_body)))
194 .unwrap()
195 }
196}
197
198
199
200#[macro_export]
201macro_rules! safe_eject {
202 ($e:expr, $err_status:expr) => {
203 $e.map_err(|x| NanoServiceError::new(x.to_string(), $err_status))
204 };
205 ($e:expr, $err_status:expr, $message_context:expr) => {
206 $e.map_err(|x| NanoServiceError::new(
207 format!("{}: {}", $message_context, x.to_string()),
208 $err_status
209 )
210 )
211 };
212}