1use reqwest::StatusCode;
2use std::fmt;
3
4#[derive(Debug, thiserror::Error)]
6pub enum ConcurError {
7 #[error("HTTP error: {0}")]
9 Http(#[from] reqwest::Error),
10
11 #[error("JSON error: {0}")]
13 Json(#[from] serde_json::Error),
14
15 #[error("URL error: {0}")]
17 Url(#[from] url::ParseError),
18
19 #[error("API error {status}: {message}")]
21 Api {
22 status: StatusCode,
23 message: String,
24 correlation_id: Option<String>,
25 },
26
27 #[error("Authentication error: {0}")]
29 Auth(String),
30
31 #[error("Retry budget exhausted after {attempts} attempts")]
33 RetryExhausted { attempts: u32 },
34
35 #[error("Missing required parameter: {0}")]
37 MissingParameter(String),
38
39 #[error("{0}")]
41 Other(String),
42}
43
44impl ConcurError {
45 pub fn api(status: StatusCode, message: impl Into<String>) -> Self {
46 Self::Api {
47 status,
48 message: message.into(),
49 correlation_id: None,
50 }
51 }
52
53 pub fn with_correlation_id(mut self, id: impl Into<String>) -> Self {
54 if let Self::Api { correlation_id, .. } = &mut self {
55 *correlation_id = Some(id.into());
56 }
57 self
58 }
59}
60
61pub type Result<T> = std::result::Result<T, ConcurError>;
63
64#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
66pub struct ConcurApiError {
67 pub error: Option<String>,
68 #[serde(rename = "error_description")]
69 pub error_description: Option<String>,
70 pub code: Option<i32>,
71 pub timestamp: Option<String>,
72 pub error_message: Option<String>,
73 pub error_id: Option<String>,
74 pub path: Option<String>,
75}
76
77impl fmt::Display for ConcurApiError {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 if let Some(msg) = &self.error_message {
80 write!(f, "{}", msg)
81 } else if let Some(desc) = &self.error_description {
82 write!(f, "{}", desc)
83 } else if let Some(err) = &self.error {
84 write!(f, "{}", err)
85 } else {
86 write!(f, "{:?}", self)
87 }
88 }
89}