1use crate::httpx;
20use crate::tracingcomponent::MetricsName;
21use http::StatusCode;
22use std::error::Error as StdError;
23use std::fmt::{Display, Formatter};
24
25pub type Result<T> = std::result::Result<T, Error>;
26
27#[derive(Debug, Clone, PartialEq)]
28pub struct Error {
29 inner: ErrorImpl,
30}
31
32impl Display for Error {
33 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
34 write!(f, "{}", self.inner.kind)
35 }
36}
37
38impl StdError for Error {}
39
40impl Error {
41 pub(crate) fn new_server_error(e: ServerError) -> Error {
42 Self {
43 inner: ErrorImpl {
44 kind: Box::new(ErrorKind::Server(e)),
45 },
46 }
47 }
48
49 pub(crate) fn new_message_error(
50 msg: impl Into<String>,
51 endpoint: impl Into<Option<String>>,
52 statement: impl Into<Option<String>>,
53 client_context_id: impl Into<Option<String>>,
54 ) -> Error {
55 Self {
56 inner: ErrorImpl {
57 kind: Box::new(ErrorKind::Message {
58 msg: msg.into(),
59 endpoint: endpoint.into(),
60 statement: statement.into(),
61 client_context_id: client_context_id.into(),
62 }),
63 },
64 }
65 }
66
67 pub(crate) fn new_encoding_error(msg: impl Into<String>) -> Error {
68 Self {
69 inner: ErrorImpl {
70 kind: Box::new(ErrorKind::Encoding { msg: msg.into() }),
71 },
72 }
73 }
74
75 pub(crate) fn new_invalid_argument_error(
76 msg: impl Into<String>,
77 arg: impl Into<Option<String>>,
78 ) -> Self {
79 Self {
80 inner: ErrorImpl {
81 kind: Box::new(ErrorKind::InvalidArgument {
82 msg: msg.into(),
83 arg: arg.into(),
84 }),
85 },
86 }
87 }
88
89 pub(crate) fn new_http_error(
90 error: httpx::error::Error,
91 endpoint: impl Into<String>,
92 statement: impl Into<Option<String>>,
93 client_context_id: impl Into<Option<String>>,
94 ) -> Self {
95 Self {
96 inner: ErrorImpl {
97 kind: Box::new(ErrorKind::Http {
98 error,
99 endpoint: endpoint.into(),
100 statement: statement.into(),
101 client_context_id: client_context_id.into(),
102 }),
103 },
104 }
105 }
106
107 pub fn kind(&self) -> &ErrorKind {
108 &self.inner.kind
109 }
110}
111
112#[derive(Debug, Clone)]
113struct ErrorImpl {
114 kind: Box<ErrorKind>,
115}
116
117impl PartialEq for ErrorImpl {
118 fn eq(&self, other: &Self) -> bool {
119 self.kind == other.kind
120 }
121}
122
123#[derive(Clone, Debug, PartialEq, Eq)]
124#[non_exhaustive]
125pub enum ErrorKind {
126 Server(ServerError),
127 #[non_exhaustive]
128 Http {
129 error: httpx::error::Error,
130 endpoint: String,
131 statement: Option<String>,
132 client_context_id: Option<String>,
133 },
134 #[non_exhaustive]
135 Message {
136 msg: String,
137 endpoint: Option<String>,
138 statement: Option<String>,
139 client_context_id: Option<String>,
140 },
141 #[non_exhaustive]
142 InvalidArgument {
143 msg: String,
144 arg: Option<String>,
145 },
146 #[non_exhaustive]
147 Encoding {
148 msg: String,
149 },
150}
151
152impl Display for ErrorKind {
153 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
154 match self {
155 ErrorKind::Server(e) => write!(f, "{e}"),
156 ErrorKind::InvalidArgument { msg, arg } => {
157 let base_msg = format!("invalid argument: {msg}");
158 if let Some(arg) = arg {
159 write!(f, "{base_msg}, arg: {arg}")
160 } else {
161 write!(f, "{base_msg}")
162 }
163 }
164 ErrorKind::Encoding { msg } => write!(f, "encoding error: {msg}"),
165 ErrorKind::Http {
166 error,
167 endpoint,
168 statement,
169 client_context_id,
170 } => {
171 write!(f, "http error {error}: endpoint: {endpoint}")?;
172 if let Some(statement) = statement {
173 write!(f, ", statement: {statement}")?;
174 }
175 if let Some(client_context_id) = client_context_id {
176 write!(f, ", client context id: {client_context_id}")?;
177 }
178 Ok(())
179 }
180 ErrorKind::Message {
181 msg,
182 endpoint,
183 statement,
184 client_context_id,
185 } => {
186 write!(f, "{msg}")?;
187 if let Some(endpoint) = endpoint {
188 write!(f, ", endpoint: {endpoint}")?;
189 }
190 if let Some(statement) = statement {
191 write!(f, ", statement: {statement}")?;
192 }
193 if let Some(client_context_id) = client_context_id {
194 write!(f, ", client context id: {client_context_id}")?;
195 }
196 Ok(())
197 }
198 }
199 }
200}
201
202#[derive(Clone, Debug, PartialEq, Eq)]
203pub struct ServerError {
204 kind: ServerErrorKind,
205
206 endpoint: String,
207 status_code: StatusCode,
208 code: u32,
209 msg: String,
210
211 statement: Option<String>,
212 client_context_id: Option<String>,
213
214 all_error_descs: Vec<ErrorDesc>,
215}
216
217impl ServerError {
218 pub(crate) fn new(
219 kind: ServerErrorKind,
220 endpoint: impl Into<String>,
221 status_code: StatusCode,
222 code: u32,
223 msg: impl Into<String>,
224 ) -> Self {
225 Self {
226 kind,
227 endpoint: endpoint.into(),
228 status_code,
229 code,
230 msg: msg.into(),
231 statement: None,
232 client_context_id: None,
233 all_error_descs: vec![],
234 }
235 }
236
237 pub fn kind(&self) -> &ServerErrorKind {
238 &self.kind
239 }
240
241 pub fn endpoint(&self) -> &str {
242 &self.endpoint
243 }
244
245 pub fn statement(&self) -> Option<&str> {
246 self.statement.as_deref()
247 }
248
249 pub fn status_code(&self) -> StatusCode {
250 self.status_code
251 }
252
253 pub fn client_context_id(&self) -> Option<&str> {
254 self.client_context_id.as_deref()
255 }
256
257 pub fn code(&self) -> u32 {
258 self.code
259 }
260
261 pub fn msg(&self) -> &str {
262 &self.msg
263 }
264
265 pub fn all_error_descs(&self) -> &[ErrorDesc] {
266 &self.all_error_descs
267 }
268
269 pub(crate) fn with_statement(mut self, statement: impl Into<String>) -> Self {
270 self.statement = Some(statement.into());
271 self
272 }
273
274 pub(crate) fn with_client_context_id(mut self, client_context_id: impl Into<String>) -> Self {
275 self.client_context_id = Some(client_context_id.into());
276 self
277 }
278
279 pub(crate) fn with_error_descs(mut self, error_descs: Vec<ErrorDesc>) -> Self {
280 self.all_error_descs = error_descs;
281 self
282 }
283}
284
285impl Display for ServerError {
286 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
287 write!(
288 f,
289 "server error of kind: {} code: {}, msg: {}",
290 self.kind, self.code, self.msg
291 )?;
292
293 if let Some(client_context_id) = &self.client_context_id {
294 write!(f, ", client context id: {client_context_id}")?;
295 }
296 if let Some(statement) = &self.statement {
297 write!(f, ", statement: {statement}")?;
298 }
299
300 write!(
301 f,
302 ", endpoint: {}, status code: {}",
303 self.endpoint, self.status_code
304 )?;
305
306 if !self.all_error_descs.is_empty() {
307 write!(f, ", all error descriptions: {:?}", self.all_error_descs)?;
308 }
309
310 Ok(())
311 }
312}
313
314#[derive(Clone, Debug, PartialEq, Eq)]
315#[non_exhaustive]
316pub struct ErrorDesc {
317 kind: ServerErrorKind,
318
319 code: u32,
320 message: String,
321}
322
323impl ErrorDesc {
324 pub fn new(kind: ServerErrorKind, code: u32, message: String) -> Self {
325 Self {
326 kind,
327 code,
328 message,
329 }
330 }
331
332 pub fn kind(&self) -> &ServerErrorKind {
333 &self.kind
334 }
335
336 pub fn code(&self) -> u32 {
337 self.code
338 }
339
340 pub fn message(&self) -> &str {
341 &self.message
342 }
343}
344
345impl Display for ErrorDesc {
346 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
347 write!(
348 f,
349 "error description of kind: {}, code: {}, message: {}",
350 self.kind, self.code, self.message
351 )
352 }
353}
354
355#[derive(Clone, Debug, PartialEq, Eq)]
356#[non_exhaustive]
357pub enum ServerErrorKind {
358 CompilationFailure,
359 Internal,
360 AuthenticationFailure,
361 ParsingFailure,
362 ScanWaitTimeout,
363 InvalidArgument { argument: String, reason: String },
364 TemporaryFailure,
365 JobQueueFull,
366 IndexNotFound,
367 IndexExists,
368 DatasetNotFound,
369 DatasetExists,
370 DataverseNotFound,
371 DataverseExists,
372 LinkNotFound,
373 LinkExists,
374 Unknown,
375}
376
377impl Display for ServerErrorKind {
378 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
379 match self {
380 ServerErrorKind::Internal => write!(f, "internal server error"),
381 ServerErrorKind::AuthenticationFailure => write!(f, "authentication failure"),
382 ServerErrorKind::InvalidArgument { argument, reason } => write!(
383 f,
384 "server invalid argument: (argument: {argument}, reason: {reason})"
385 ),
386 ServerErrorKind::Unknown => write!(f, "unknown query error"),
387 ServerErrorKind::CompilationFailure => write!(f, "compilation failure"),
388 ServerErrorKind::ParsingFailure => write!(f, "parsing failure"),
389 ServerErrorKind::ScanWaitTimeout => write!(f, "scan wait timeout"),
390 ServerErrorKind::TemporaryFailure => write!(f, "temporary failure"),
391 ServerErrorKind::DatasetExists => write!(f, "dataset exists"),
392 ServerErrorKind::DatasetNotFound => write!(f, "dataset not found"),
393 ServerErrorKind::DataverseExists => write!(f, "dataverse exists"),
394 ServerErrorKind::DataverseNotFound => write!(f, "dataverse not found"),
395 ServerErrorKind::IndexExists => write!(f, "index exists"),
396 ServerErrorKind::IndexNotFound => write!(f, "index not found"),
397 ServerErrorKind::LinkExists => write!(f, "link exists"),
398 ServerErrorKind::LinkNotFound => write!(f, "link not found"),
399 ServerErrorKind::JobQueueFull => write!(f, "job queue full"),
400 }
401 }
402}
403
404impl MetricsName for Error {
405 fn metrics_name(&self) -> &'static str {
406 match self.kind() {
407 ErrorKind::Server(e) => e.kind().metrics_name(),
408 ErrorKind::Http { error, .. } => error.metrics_name(),
409 ErrorKind::Message { .. } => "analyticsx._OTHER",
410 ErrorKind::InvalidArgument { .. } => "analyticsx.InvalidArgument",
411 ErrorKind::Encoding { .. } => "analyticsx.Encoding",
412 }
413 }
414}
415
416impl MetricsName for ServerErrorKind {
417 fn metrics_name(&self) -> &'static str {
418 match self {
419 ServerErrorKind::CompilationFailure => "analyticsx.CompilationFailure",
420 ServerErrorKind::Internal => "analyticsx.Internal",
421 ServerErrorKind::AuthenticationFailure => "analyticsx.AuthenticationFailure",
422 ServerErrorKind::ParsingFailure => "analyticsx.ParsingFailure",
423 ServerErrorKind::ScanWaitTimeout => "analyticsx.ScanWaitTimeout",
424 ServerErrorKind::InvalidArgument { .. } => "analyticsx.InvalidArgument",
425 ServerErrorKind::TemporaryFailure => "analyticsx.TemporaryFailure",
426 ServerErrorKind::JobQueueFull => "analyticsx.JobQueueFull",
427 ServerErrorKind::IndexNotFound => "analyticsx.IndexNotFound",
428 ServerErrorKind::IndexExists => "analyticsx.IndexExists",
429 ServerErrorKind::DatasetNotFound => "analyticsx.DatasetNotFound",
430 ServerErrorKind::DatasetExists => "analyticsx.DatasetExists",
431 ServerErrorKind::DataverseNotFound => "analyticsx.DataverseNotFound",
432 ServerErrorKind::DataverseExists => "analyticsx.DataverseExists",
433 ServerErrorKind::LinkNotFound => "analyticsx.LinkNotFound",
434 ServerErrorKind::LinkExists => "analyticsx.LinkExists",
435 ServerErrorKind::Unknown => "analyticsx._OTHER",
436 }
437 }
438}