use http::StatusCode;
use url::Url;
use super::BoxError;
use super::truncate_for_display;
use crate::json::JsonValue;
use crate::shared::Headers;
const DISPLAY_MAX_BYTES: usize = 2048;
#[must_use]
pub fn default_retryable(status_code: Option<StatusCode>) -> bool {
match status_code {
None => true,
Some(status) => {
status == StatusCode::REQUEST_TIMEOUT
|| status == StatusCode::CONFLICT
|| status == StatusCode::TOO_MANY_REQUESTS
|| status.is_server_error()
}
}
}
#[derive(Debug)]
pub struct ApiCallError {
pub message: String,
pub url: Url,
pub request_body: Option<JsonValue>,
pub status_code: Option<StatusCode>,
pub response_headers: Option<Headers>,
pub response_body: Option<String>,
pub is_retryable: bool,
pub data: Option<JsonValue>,
pub cause: Option<BoxError>,
}
impl ApiCallError {
#[must_use]
pub fn new(message: impl Into<String>, url: Url) -> Self {
Self {
message: message.into(),
url,
request_body: None,
status_code: None,
response_headers: None,
response_body: None,
is_retryable: default_retryable(None),
data: None,
cause: None,
}
}
#[must_use]
pub fn with_status(mut self, status_code: StatusCode) -> Self {
self.status_code = Some(status_code);
self.is_retryable = default_retryable(Some(status_code));
self
}
#[must_use]
pub fn with_request_body(mut self, body: JsonValue) -> Self {
self.request_body = Some(body);
self
}
#[must_use]
pub fn with_response(mut self, headers: Headers, body: Option<String>) -> Self {
self.response_headers = Some(headers);
self.response_body = body;
self
}
#[must_use]
pub fn with_data(mut self, data: JsonValue) -> Self {
self.data = Some(data);
self
}
#[must_use]
pub fn with_cause(mut self, cause: impl std::error::Error + Send + Sync + 'static) -> Self {
self.cause = Some(Box::new(cause));
self
}
#[must_use]
pub fn retryable(mut self, is_retryable: bool) -> Self {
self.is_retryable = is_retryable;
self
}
}
impl std::fmt::Display for ApiCallError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&truncate_for_display(&self.message, DISPLAY_MAX_BYTES))
}
}
impl std::error::Error for ApiCallError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.cause
.as_deref()
.map(|cause| cause as &(dyn std::error::Error + 'static))
}
}