1use reqwest::StatusCode;
2
3pub type Result<T> = std::result::Result<T, UniError>;
5
6#[derive(Debug, thiserror::Error)]
8pub enum UniError {
9 #[error("[{code}] {message}{request_suffix}", request_suffix = format_request_id(.request_id.as_deref()))]
11 Api {
12 code: String,
14 message: String,
16 status: StatusCode,
18 request_id: Option<String>,
20 body: Option<serde_json::Value>,
22 },
23
24 #[error("HTTP {status}: {message}{request_suffix}", request_suffix = format_request_id(.request_id.as_deref()))]
26 Http {
27 status: StatusCode,
29 message: String,
31 request_id: Option<String>,
33 body: Option<serde_json::Value>,
35 },
36
37 #[error("request failed: {0}")]
39 Transport(#[source] reqwest::Error),
40
41 #[error("invalid configuration: {0}")]
43 Configuration(String),
44
45 #[error("invalid API response (HTTP {status}){request_suffix}: {message}", request_suffix = format_request_id(.request_id.as_deref()))]
47 InvalidResponse {
48 status: StatusCode,
50 request_id: Option<String>,
52 message: String,
54 body: String,
56 },
57}
58
59impl From<reqwest::Error> for UniError {
60 fn from(error: reqwest::Error) -> Self {
61 Self::Transport(error.without_url())
64 }
65}
66
67impl UniError {
68 pub fn code(&self) -> Option<&str> {
70 match self {
71 Self::Api { code, .. } => Some(code),
72 _ => None,
73 }
74 }
75
76 pub fn status(&self) -> Option<StatusCode> {
78 match self {
79 Self::Api { status, .. }
80 | Self::Http { status, .. }
81 | Self::InvalidResponse { status, .. } => Some(*status),
82 _ => None,
83 }
84 }
85
86 pub fn request_id(&self) -> Option<&str> {
88 match self {
89 Self::Api { request_id, .. }
90 | Self::Http { request_id, .. }
91 | Self::InvalidResponse { request_id, .. } => request_id.as_deref(),
92 _ => None,
93 }
94 }
95}
96
97fn format_request_id(request_id: Option<&str>) -> String {
98 request_id
99 .map(|id| format!(", RequestId: {id}"))
100 .unwrap_or_default()
101}