relay_server/utils/
api.rs

1use std::error::Error;
2use std::fmt;
3
4use axum::response::IntoResponse;
5use serde::{Deserialize, Serialize};
6
7/// Represents an action requested by the Upstream sent in an error message.
8#[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/// An error response from an api.
24#[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    /// Creates an error response with a detail message
36    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}