1use actix_http::body::BoxBody;
2use actix_web::{error, HttpResponse, ResponseError};
3use http::StatusCode;
4use serde::{Deserialize, Serialize};
5use serde_with::{serde_as, FromInto};
6use std::fmt::Display;
7use tonic::{Code, Status};
8
9use crate::http_compatibility::to_actix_status;
10
11#[serde_as]
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
13pub struct Error {
14 #[serde_as(as = "FromInto<i32>")]
15 pub code: Code,
16 pub message: String,
17}
18
19impl Error {
20 pub fn map_tonic_code(code: Code) -> StatusCode {
21 use tonic::Code::*;
22 match code {
23 Ok => StatusCode::OK,
24 Cancelled => StatusCode::from_u16(499).unwrap(),
25 Unknown => StatusCode::INTERNAL_SERVER_ERROR,
26 InvalidArgument => StatusCode::BAD_REQUEST,
27 DeadlineExceeded => StatusCode::GATEWAY_TIMEOUT,
28 NotFound => StatusCode::NOT_FOUND,
29 AlreadyExists => StatusCode::CONFLICT,
30 PermissionDenied => StatusCode::FORBIDDEN,
31 ResourceExhausted => StatusCode::TOO_MANY_REQUESTS,
32 FailedPrecondition => StatusCode::BAD_REQUEST,
33 Aborted => StatusCode::CONFLICT,
34 OutOfRange => StatusCode::BAD_REQUEST,
35 Unimplemented => StatusCode::NOT_IMPLEMENTED,
36 Internal => StatusCode::INTERNAL_SERVER_ERROR,
37 Unavailable => StatusCode::SERVICE_UNAVAILABLE,
38 DataLoss => StatusCode::INTERNAL_SERVER_ERROR,
39 Unauthenticated => StatusCode::UNAUTHORIZED,
40 }
41 }
42
43 pub fn from_actix(err: error::Error, code: Code) -> Self {
44 Self {
45 code,
46 message: err.to_string(),
47 }
48 }
49}
50
51impl Display for Error {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 write!(f, "{}: {}", self.code, self.message)
54 }
55}
56
57impl ResponseError for Error {
58 fn status_code(&self) -> actix_web::http::StatusCode {
59 to_actix_status(Self::map_tonic_code(self.code))
60 }
61
62 fn error_response(&self) -> HttpResponse<BoxBody> {
63 let body = serde_json::to_string(self);
64 match body {
65 Ok(body) => HttpResponse::build(self.status_code())
66 .content_type("application/json")
67 .body(body),
68 Err(err) => {
69 let body = format!(
70 r#"{{"code":{}, "message":"while serializing error another error happened: {}, original error: {}"}}"#,
71 i32::from(self.code),
72 err,
73 self
74 );
75 HttpResponse::build(actix_http::StatusCode::INTERNAL_SERVER_ERROR)
76 .content_type("application/json")
77 .body(body)
78 }
79 }
80 }
81}
82
83impl From<Status> for Error {
84 fn from(value: Status) -> Self {
85 Self {
86 code: value.code(),
87 message: value.message().to_owned(),
88 }
89 }
90}