plane/controller/
error.rs1use axum::{
2 http::StatusCode,
3 response::{IntoResponse, Response},
4 Json,
5};
6use plane_common::{
7 protocol::{ApiError, ApiErrorKind},
8 util::random_string,
9};
10use std::{error::Error, fmt::Debug};
11
12pub fn err_to_response<E: Debug>(
13 error: E,
14 status: StatusCode,
15 user_message: &str,
16 code: ApiErrorKind,
17) -> Response {
18 let err_id = random_string();
19
20 if status.is_server_error() {
21 tracing::error!(
22 ?err_id,
23 ?error,
24 "Server error encountered while handling request."
25 );
26 } else {
27 tracing::warn!(
28 ?err_id,
29 ?error,
30 "Client error encountered while handling request."
31 );
32 }
33
34 let result = ApiError {
35 id: err_id.clone(),
36 message: user_message.to_string(),
37 kind: code,
38 };
39
40 (status, Json(result)).into_response()
41}
42
43pub trait IntoApiError<T>: Sized {
44 fn or_not_found(self, user_message: &str) -> Result<T, Response> {
45 self.or_status(StatusCode::NOT_FOUND, user_message, ApiErrorKind::NotFound)
46 }
47
48 fn or_internal_error(self, user_message: &str) -> Result<T, Response> {
49 self.or_status(
50 StatusCode::INTERNAL_SERVER_ERROR,
51 user_message,
52 ApiErrorKind::Other,
53 )
54 }
55
56 fn or_status(
57 self,
58 status: StatusCode,
59 user_message: &str,
60 code: ApiErrorKind,
61 ) -> Result<T, Response>;
62}
63
64impl<T, E: Error> IntoApiError<T> for Result<T, E> {
65 fn or_status(
66 self,
67 status: StatusCode,
68 user_message: &str,
69 code: ApiErrorKind,
70 ) -> Result<T, Response> {
71 match self {
72 Ok(v) => Ok(v),
73 Err(error) => {
74 let err = err_to_response(&error, status, user_message, code);
75 Err(err)
76 }
77 }
78 }
79}
80
81impl<T> IntoApiError<T> for Option<T> {
82 fn or_status(
83 self,
84 status: StatusCode,
85 user_message: &str,
86 code: ApiErrorKind,
87 ) -> Result<T, Response> {
88 match self {
89 Some(v) => Ok(v),
90 None => {
91 let err = err_to_response("Missing value.", status, user_message, code);
92 Err(err)
93 }
94 }
95 }
96}