use serde::Deserialize;
pub type ApifyClientResult<T> = Result<T, ApifyClientError>;
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct ApiErrorBody {
pub error: ApiErrorDetail,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct ApiErrorDetail {
#[serde(rename = "type")]
pub error_type: Option<String>,
pub message: Option<String>,
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone)]
pub struct ApiError {
pub status_code: u16,
pub error_type: Option<String>,
pub message: String,
pub attempt: u32,
pub http_method: Option<String>,
pub path: Option<String>,
pub data: Option<serde_json::Value>,
}
impl std::fmt::Display for ApiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Apify API error (status {}, type {}): {}",
self.status_code,
self.error_type.as_deref().unwrap_or("unknown"),
self.message,
)
}
}
impl std::error::Error for ApiError {}
#[derive(Debug, thiserror::Error)]
pub enum ApifyClientError {
#[error(transparent)]
Api(Box<ApiError>),
#[error("HTTP transport error: {0}")]
Http(String),
#[error("Request timed out")]
Timeout,
#[error("(De)serialization error: {0}")]
Serde(#[from] serde_json::Error),
#[error("Invalid response: {0}")]
InvalidResponse(String),
#[error("Invalid argument: {0}")]
InvalidArgument(String),
}
impl From<ApiError> for ApifyClientError {
fn from(err: ApiError) -> Self {
ApifyClientError::Api(Box::new(err))
}
}
impl ApifyClientError {
pub fn as_api_error(&self) -> Option<&ApiError> {
match self {
ApifyClientError::Api(e) => Some(e),
_ => None,
}
}
pub fn status_code(&self) -> Option<u16> {
self.as_api_error().map(|e| e.status_code)
}
}
impl From<reqwest::Error> for ApifyClientError {
fn from(err: reqwest::Error) -> Self {
if err.is_timeout() {
ApifyClientError::Timeout
} else {
ApifyClientError::Http(err.to_string())
}
}
}