1use thiserror::Error;
2
3#[derive(Error, Debug)]
4pub enum ApiError {
5 #[error("UnprocessableEntityError: Unprocessable entity - {{message}}")]
6 UnprocessableEntityError {
7 message: String,
8 field: Option<String>,
9 validation_error: Option<String>,
10 },
11 #[error("NotFoundError: Resource not found - {{message}}")]
12 NotFoundError {
13 message: String,
14 resource_id: Option<String>,
15 resource_type: Option<String>,
16 },
17 #[error("BadRequestError: Bad request - {{message}}")]
18 BadRequestError {
19 message: String,
20 field: Option<String>,
21 details: Option<String>,
22 },
23 #[error("ForbiddenError: Access forbidden - {{message}}")]
24 ForbiddenError {
25 message: String,
26 resource: Option<String>,
27 required_permission: Option<String>,
28 },
29 #[error("ConflictError: Conflict - {{message}}")]
30 ConflictError {
31 message: String,
32 conflict_type: Option<String>,
33 },
34 #[error("InternalServerError: Internal server error - {{message}}")]
35 InternalServerError {
36 message: String,
37 error_id: Option<String>,
38 },
39 #[error("HTTP error {status}: {message}")]
40 Http { status: u16, message: String },
41 #[error("Network error: {0}")]
42 Network(reqwest::Error),
43 #[error("Serialization error: {0}")]
44 Serialization(serde_json::Error),
45 #[error("Configuration error: {0}")]
46 Configuration(String),
47 #[error("Invalid header value")]
48 InvalidHeader,
49 #[error("Could not clone request for retry")]
50 RequestClone,
51 #[error("SSE stream terminated")]
52 StreamTerminated,
53 #[error("SSE stream timed out waiting for next event")]
54 StreamTimeout,
55 #[error("SSE parse error: {0}")]
56 SseParseError(String),
57}
58
59impl ApiError {
60 pub fn from_response(status_code: u16, body: Option<&str>) -> Self {
61 match status_code {
62 422 => {
63 if let Some(body_str) = body {
65 if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
66 return Self::UnprocessableEntityError {
67 message: parsed
68 .get("message")
69 .and_then(|v| v.as_str())
70 .unwrap_or("Unknown error")
71 .to_string(),
72 field: parsed
73 .get("field")
74 .and_then(|v| v.as_str().map(|s| s.to_string())),
75 validation_error: parsed
76 .get("validation_error")
77 .and_then(|v| v.as_str().map(|s| s.to_string())),
78 };
79 }
80 }
81 return Self::UnprocessableEntityError {
82 message: body.unwrap_or("Unknown error").to_string(),
83 field: None,
84 validation_error: None,
85 };
86 }
87 404 => {
88 if let Some(body_str) = body {
90 if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
91 return Self::NotFoundError {
92 message: parsed
93 .get("message")
94 .and_then(|v| v.as_str())
95 .unwrap_or("Unknown error")
96 .to_string(),
97 resource_id: parsed
98 .get("resource_id")
99 .and_then(|v| v.as_str().map(|s| s.to_string())),
100 resource_type: parsed
101 .get("resource_type")
102 .and_then(|v| v.as_str().map(|s| s.to_string())),
103 };
104 }
105 }
106 return Self::NotFoundError {
107 message: body.unwrap_or("Unknown error").to_string(),
108 resource_id: None,
109 resource_type: None,
110 };
111 }
112 400 => {
113 if let Some(body_str) = body {
115 if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
116 return Self::BadRequestError {
117 message: parsed
118 .get("message")
119 .and_then(|v| v.as_str())
120 .unwrap_or("Unknown error")
121 .to_string(),
122 field: parsed
123 .get("field")
124 .and_then(|v| v.as_str().map(|s| s.to_string())),
125 details: parsed
126 .get("details")
127 .and_then(|v| v.as_str().map(|s| s.to_string())),
128 };
129 }
130 }
131 return Self::BadRequestError {
132 message: body.unwrap_or("Unknown error").to_string(),
133 field: None,
134 details: None,
135 };
136 }
137 403 => {
138 if let Some(body_str) = body {
140 if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
141 return Self::ForbiddenError {
142 message: parsed
143 .get("message")
144 .and_then(|v| v.as_str())
145 .unwrap_or("Unknown error")
146 .to_string(),
147 resource: parsed
148 .get("resource")
149 .and_then(|v| v.as_str().map(|s| s.to_string())),
150 required_permission: parsed
151 .get("required_permission")
152 .and_then(|v| v.as_str().map(|s| s.to_string())),
153 };
154 }
155 }
156 return Self::ForbiddenError {
157 message: body.unwrap_or("Unknown error").to_string(),
158 resource: None,
159 required_permission: None,
160 };
161 }
162 409 => {
163 if let Some(body_str) = body {
165 if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
166 return Self::ConflictError {
167 message: parsed
168 .get("message")
169 .and_then(|v| v.as_str())
170 .unwrap_or("Unknown error")
171 .to_string(),
172 conflict_type: parsed
173 .get("conflict_type")
174 .and_then(|v| v.as_str().map(|s| s.to_string())),
175 };
176 }
177 }
178 return Self::ConflictError {
179 message: body.unwrap_or("Unknown error").to_string(),
180 conflict_type: None,
181 };
182 }
183 500 => {
184 if let Some(body_str) = body {
186 if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
187 return Self::InternalServerError {
188 message: parsed
189 .get("message")
190 .and_then(|v| v.as_str())
191 .unwrap_or("Unknown error")
192 .to_string(),
193 error_id: parsed
194 .get("error_id")
195 .and_then(|v| v.as_str().map(|s| s.to_string())),
196 };
197 }
198 }
199 return Self::InternalServerError {
200 message: body.unwrap_or("Unknown error").to_string(),
201 error_id: None,
202 };
203 }
204 _ => Self::Http {
205 status: status_code,
206 message: body.unwrap_or("Unknown error").to_string(),
207 },
208 }
209 }
210}
211
212#[derive(Debug)]
214pub struct BuildError {
215 field: &'static str,
216}
217
218impl BuildError {
219 pub fn missing_field(field: &'static str) -> Self {
220 Self { field }
221 }
222}
223
224impl std::fmt::Display for BuildError {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 write!(f, "`{}` was not set but is required", self.field)
227 }
228}
229
230impl std::error::Error for BuildError {}