Skip to main content

voltaria_sdk/
error.rs

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                // Parse error body for UnprocessableEntityError;
64                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                // Parse error body for NotFoundError;
89                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                // Parse error body for BadRequestError;
114                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                // Parse error body for ForbiddenError;
139                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                // Parse error body for ConflictError;
164                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                // Parse error body for InternalServerError;
185                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/// Error returned when a required field was not set on a builder.
213#[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 {}