actix_prost/
error.rs

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}