relay_server/utils/
api.rs1use std::error::Error;
2use std::fmt;
3
4use axum::response::IntoResponse;
5use serde::{Deserialize, Serialize};
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, Default)]
9#[serde(rename_all = "camelCase")]
10pub enum RelayErrorAction {
11 Stop,
12 #[serde(other)]
13 #[default]
14 None,
15}
16
17impl RelayErrorAction {
18 fn is_none(&self) -> bool {
19 *self == Self::None
20 }
21}
22
23#[derive(Serialize, Deserialize, Default, Debug)]
25pub struct ApiErrorResponse {
26 #[serde(default)]
27 detail: Option<String>,
28 #[serde(default, skip_serializing_if = "Vec::is_empty")]
29 causes: Vec<String>,
30 #[serde(default, skip_serializing_if = "RelayErrorAction::is_none")]
31 relay: RelayErrorAction,
32}
33
34impl ApiErrorResponse {
35 pub fn with_detail<S: AsRef<str>>(s: S) -> ApiErrorResponse {
37 ApiErrorResponse {
38 detail: Some(s.as_ref().to_string()),
39 causes: Vec::new(),
40 relay: RelayErrorAction::None,
41 }
42 }
43
44 pub fn from_error<E: Error + ?Sized>(error: &E) -> Self {
45 let detail = Some(error.to_string());
46
47 let mut causes = Vec::new();
48 let mut source = error.source();
49 while let Some(s) = source {
50 causes.push(s.to_string());
51 source = s.source();
52 }
53
54 Self {
55 detail,
56 causes,
57 relay: RelayErrorAction::None,
58 }
59 }
60
61 pub fn relay_action(&self) -> RelayErrorAction {
62 self.relay
63 }
64}
65
66impl fmt::Display for ApiErrorResponse {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 if let Some(ref detail) = self.detail {
69 write!(f, "{detail}")
70 } else {
71 write!(f, "no error details")
72 }
73 }
74}
75
76impl Error for ApiErrorResponse {}
77
78impl IntoResponse for ApiErrorResponse {
79 fn into_response(self) -> axum::response::Response {
80 axum::Json(self).into_response()
81 }
82}