use std::fmt;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
Response(#[from] ErrorResponse),
#[error(transparent)]
HttpClient(#[from] reqwest::Error),
#[error("token source failed: {0}")]
TokenSource(Box<dyn std::error::Error + Send + Sync>),
}
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorResponse {
pub code: u16,
pub errors: Option<Vec<ErrorResponseItem>>,
pub message: String,
}
const RETRYABLE_CODES: [u16; 4] = [500, 502, 503, 504];
impl ErrorResponse {
pub fn is_retryable(&self, retryable_reasons: &[&str]) -> bool {
if RETRYABLE_CODES.contains(&self.code) {
return true;
}
match &self.errors {
None => false,
Some(details) => {
for detail in details {
for reason in retryable_reasons {
if &detail.reason == reason {
return true;
}
}
}
false
}
}
}
}
impl fmt::Display for ErrorResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.message.fmt(f)
}
}
impl std::error::Error for ErrorResponse {}
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorResponseItem {
pub message: String,
pub reason: String,
}
impl fmt::Display for ErrorResponseItem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.message.fmt(f)
}
}
#[derive(serde::Deserialize)]
pub(crate) struct ErrorWrapper {
pub(crate) error: ErrorResponse,
}