google_cloud_bigquery/http/
error.rs1use std::fmt;
2
3#[derive(thiserror::Error, Debug)]
4pub enum Error {
5 #[error(transparent)]
7 Response(#[from] ErrorResponse),
8
9 #[error(transparent)]
11 HttpClient(#[from] reqwest::Error),
12
13 #[error(transparent)]
15 HttpMiddleware(anyhow::Error),
16
17 #[error("token source failed: {0}")]
19 TokenSource(Box<dyn std::error::Error + Send + Sync>),
20}
21
22impl From<reqwest_middleware::Error> for Error {
23 fn from(error: reqwest_middleware::Error) -> Self {
24 match error {
25 reqwest_middleware::Error::Reqwest(err) => Error::HttpClient(err),
26 reqwest_middleware::Error::Middleware(err) => Error::HttpMiddleware(err),
27 }
28 }
29}
30
31#[derive(Debug, serde::Deserialize)]
32#[serde(rename_all = "camelCase")]
33pub struct ErrorResponse {
34 pub code: u16,
38
39 pub errors: Option<Vec<ErrorResponseItem>>,
41
42 pub message: String,
44}
45
46const RETRYABLE_CODES: [u16; 4] = [500, 502, 503, 504];
47
48impl ErrorResponse {
49 pub fn is_retryable(&self, retryable_reasons: &[&str]) -> bool {
50 if RETRYABLE_CODES.contains(&self.code) {
51 return true;
52 }
53 match &self.errors {
54 None => false,
55 Some(details) => {
56 for detail in details {
57 for reason in retryable_reasons {
58 if &detail.reason == reason {
59 return true;
60 }
61 }
62 }
63 false
64 }
65 }
66 }
67}
68
69impl fmt::Display for ErrorResponse {
70 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71 self.message.fmt(f)
72 }
73}
74
75impl std::error::Error for ErrorResponse {}
76
77#[derive(Debug, serde::Deserialize)]
79#[serde(rename_all = "camelCase")]
80pub struct ErrorResponseItem {
81 pub message: String,
83
84 pub reason: String,
86}
87
88impl fmt::Display for ErrorResponseItem {
89 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90 self.message.fmt(f)
91 }
92}
93
94#[derive(serde::Deserialize)]
95pub(crate) struct ErrorWrapper {
96 pub(crate) error: ErrorResponse,
97}